Validation Reference
When you submit a BodyLink game, the fail-closed submission gate checks your bundle and — on rejection — names every failing check along with a one-line recommended fix. This page is the reference for those named checks: the code the gate emits, what it means, and the fix.
The report shape is:
{ slug, pass, checks: [ { check, pass, issues: [ { code, message, fix } ] } ] }
Run the gate locally to get the same report the developer portal shows:
node scripts/validate-submission.mjs <bundle-dir> --report
Each issue belongs to a named check group. The tables below are grouped the same way. Any code not listed falls back to a generic fix ("review the validation message and the Runtime v1 bundle contract, then re-run the validator") — a failing check is never left without a fix.
Manifest & metadata
Checks on bodylink.game.json: the entry shape, the launcher block, and
tracking metadata.
| Code | What it means | Recommended fix |
|---|---|---|
bundle.manifest_missing |
no readable manifest | Add a valid bodylink.game.json at the bundle root (valid JSON). |
bundle.manifest_invalid |
manifest does not parse / match schema | Fix the bodylink.game.json so it parses and matches the manifest schema. |
bundle.manifest_file_missing |
manifest absent from the exported bundle | Include bodylink.game.json in the exported bundle. |
bundle.entry_kind |
entry.kind is not "iframe" |
Set manifest entry.kind to "iframe" for Runtime v1 bundles. |
bundle.entry_missing |
no entry.html |
Add manifest entry.html pointing at the bundle HTML entrypoint. |
bundle.launcher_missing |
no launcher block |
Add a launcher block to the manifest for catalog-ready bundles. |
bundle.launcher_field_missing |
a launcher field is absent |
Fill the named launcher field (description / color / gridSize / tags / howToPlay). |
bundle.launcher_metadata_missing |
required top-level metadata absent | Provide the required manifest metadata (id, name, version, runtimeRange). |
bundle.launcher_metadata_invalid |
version is not valid SemVer |
Make manifest.version a valid SemVer (e.g. 1.0.0). |
bundle.launcher_tracking_focus |
invalid launcher.trackingFocus |
Set launcher.trackingFocus to one of: hand, body, none. |
bundle.launcher_tracking_preset |
preset missing when a camera is required | Set launcher.trackingPreset to a valid preset when requiresCamera is true. |
bundle.launcher_tracking_tier |
invalid launcher.trackingTier |
Set launcher.trackingTier to one of: standard, high, custom. |
bundle.launcher_tracking_policy_invalid |
trackingPolicy is not an object |
Make launcher.trackingPolicy an object when declared. |
bundle.launcher_tracking_custom_policy_missing |
custom tier without a policy |
Provide an explicit launcher.trackingPolicy for trackingTier: "custom". |
bundle.launcher_tracking_policy_profile |
invalid trackingPolicy.profile |
Set trackingPolicy.profile to one of: stability, latency, precise, balanced. |
bundle.launcher_tracking_pose_model |
invalid trackingPolicy.poseModel |
Set trackingPolicy.poseModel to one of: lite, full. |
bundle.launcher_tracking_policy_tier |
raw budgets without custom tier |
Raw trackingPolicy budgets require launcher.trackingTier: "custom". |
Capabilities & SDK compatibility
Checks that your bundle declares the iframe/SDK capabilities and stays within the SDK surface (no host-internal references, all module specifiers resolved).
| Code | What it means | Recommended fix |
|---|---|---|
bundle.capability_missing |
iframe/SDK capabilities not declared | Declare the required capabilities (iframe.bundle.v1, sdk.proxy.v1). |
bundle.tracking_invalid |
manifest tracking block invalid | Fix the manifest tracking block so it matches the tracking schema. |
bundle.tracking_optional_invalid |
optional tracking declaration invalid | Correct the optional tracking declaration to a valid shape. |
bundle.tracking_optional_capability_missing |
optional feature lacks its capability | Add the capability that the declared optional tracking feature requires. |
bundle.sdk_facade_missing |
the SDK facade is missing | Keep the SDK facade in the bundle, or externalize the SDK via the lock's external-SDK descriptor. |
bundle.sdk_only_forbidden_reference |
imports a host-internal package | Remove the host-internal reference; game bundles may use only the game-kit / iframe-sdk surface. |
bundle.sdk_only_unresolved_module_import |
a bare/unresolved import (unbuilt source) | Bundle or remove the unresolved module import; SDK-only bundles must resolve all specifiers. |
bundle.externalize_sdk_integrity_required |
externalizing the SDK without an SRI | Provide the SDK SRI integrity before externalizing the SDK. |
Catalog assets
Checks on the catalog thumbnail and preview imagery.
| Code | What it means | Recommended fix |
|---|---|---|
bundle.catalog_invalid |
manifest.catalog is not an object |
Make manifest.catalog an object when declared. |
bundle.catalog_asset_missing |
thumbnail/preview missing | Add the missing catalog asset (thumbnail and preview) as bundle-relative files. |
bundle.catalog_asset_metadata |
image media metadata absent | Declare image media metadata (type: image/*) for the catalog asset. |
Asset sizes
| Code | What it means | Recommended fix |
|---|---|---|
bundle.asset_too_large |
a bundle file exceeds the per-file limit | Compress or split the file so each bundle file is under the per-file size limit. |
Bundle structure & offline safety
Checks that every path is bundle-relative and offline-safe, the iframe session is locked down, and there are no runtime module loads (which opaque-origin iframes cannot satisfy).
| Code | What it means | Recommended fix |
|---|---|---|
bundle.asset_missing |
a manifest asset path does not resolve | Ensure every manifest asset path resolves to an existing bundle file. |
bundle.path_empty |
an empty bundle-relative path | Provide a non-empty bundle-relative path. |
bundle.path_not_relative |
an absolute or URL-like path | Use a bundle-relative path (no absolute or URL-like paths). |
bundle.path_traversal |
a .. traversal segment |
Remove .. traversal segments from the path. |
bundle.iframe_session |
invalid iframeSession block |
Provide a valid manifest.iframeSession block. |
bundle.parent_origins |
allowedParentOrigins not declared |
Declare iframeSession.allowedParentOrigins explicitly. |
bundle.parent_origin_wildcard |
allowedParentOrigins contains "*" |
Replace the wildcard parent origin with explicit origins. |
bundle.sandbox |
bad iframeSession.sandbox |
Set a valid iframeSession.sandbox (allow-scripts only; never allow-same-origin). |
bundle.network_not_offline |
network not disabled | Set permissions.network to false for offline Runtime v1 bundles. |
bundle.html_external_reference |
HTML references an external URL | Make HTML references bundle-relative; external URLs are not allowed in offline bundles. |
bundle.html_reference_missing |
HTML points at a missing file | Point the HTML reference at an existing bundle file. |
bundle.opaque_origin_module_script |
a module <script> entrypoint |
Drop module <script> entrypoints in allow-scripts-only bundles (opaque-origin modules need CORS). |
bundle.opaque_origin_module_script_not_bundle_relative |
module <script> src not bundle-relative |
Make the module script src bundle-relative. |
bundle.opaque_origin_import_meta |
import.meta usage |
Remove import.meta usage; it is unavailable in opaque-origin bundles. |
bundle.opaque_origin_dynamic_import |
dynamic import() at runtime |
Remove dynamic import(); opaque-origin bundles cannot fetch modules at runtime. |
bundle.zip_invalid_path |
bad zip entry path | Fix the zip entry path (bundle-relative, no traversal). |
bundle.zip_invalid |
malformed archive | Re-export the bundle zip; the archive is malformed. |
bundle.zip_path_traversal |
traversal in a zip entry | Remove traversal segments from zip entry paths. |
bundle.zip_unsupported_method |
unsupported compression method | Re-export using a supported zip compression method (store or deflate). |
bundle.zip_size_mismatch |
entry size/hash mismatch | Re-export the zip; an entry size/hash does not match the lock. |
Offline integrity & lock
Checks on the integrity lock (bodylink.bundle.lock.json) and the index.html
entry. These almost always mean "re-export — do not hand-edit the lock."
| Code | What it means | Recommended fix |
|---|---|---|
bundle.index_missing |
no index.html |
Include index.html in the bundle. |
bundle.lock_missing |
no lock file | Include bodylink.bundle.lock.json in the exported bundle. |
bundle.lock_version |
stale lock version | Re-export with the current bundle lock version. |
bundle.lock_boundary |
lock boundary mismatch | Re-export; the lock's export boundary does not match the bundle contents. |
bundle.lock_file_hash_missing |
a file hash is missing | Re-export; the lock is missing a file hash. |
bundle.lock_asset_hash_missing |
an asset hash is missing | Re-export; the lock is missing an asset hash. |
bundle.lock_invalid |
malformed lock | Re-export; bodylink.bundle.lock.json is malformed. |
Runtime boot
The optional runtime check boots your bundle for real in a headless browser. It is skip-clean (not a failure) when no browser is available locally — but the portal's CI runner always runs it.
| Code | What it means | Recommended fix |
|---|---|---|
runtime.boot |
the live boot did not reach a healthy state | Boot locally (npm run dev:hosted), open the bundle, and resolve the SDK-handshake / tracking-frame / console failures before resubmitting. |
Process-level
Aggregate codes emitted when a validation stage fails as a whole.
| Code | What it means | Recommended fix |
|---|---|---|
bundle.validation_failed |
one or more validation issues | Resolve the reported validation issues before exporting. |
bundle.sdk_only_validation_failed |
one or more SDK-only issues | Resolve the SDK-only validation issues before exporting. |
bundle.zip_validation_failed |
one or more zip issues | Resolve the validation issues before producing the bundle zip. |
For the build, validation, and submission workflow these codes fit into, see the Developer Guide.