Source maps
Preview · Turn production JavaScript frames into the matching source location.
The upload CLI is an implementation preview. No published CLI package is assumed here. Use a reviewed SDK checkout to build it; the published rc.3 adapter does not capture JavaScript crashes.
Source maps make a captured JavaScript stack readable. They do not add crash hooks or native crash coverage.
Match the running build
Configure the same identity in the app and the upload job:
const options = {
release: {
appVersion: "1.2.3",
nativeBuild: "42",
bundleVersion: "build-abc123",
},
};These are synthetic values. Generate the real metadata once in your build pipeline. Match platform, app version, native build and immutable JavaScript bundle identity exactly. An OTA channel or runtime compatibility version is not a unique bundle identity.
For Expo, use the final native build number actually chosen by EAS. Do not guess an auto-incremented value from local app configuration. If multiple native builds receive the same OTA update, register the pair for each exact release identity.
Prepare the final artifacts
For a Hermes Android release, typical output paths are:
android/app/build/generated/assets/react/release/index.android.bundle
android/app/build/generated/sourcemaps/react/release/index.android.bundle.mapPaths vary with the build variant. Use the final Hermes bundle and composed source map, not an intermediate *.packager.map alone.
On iOS, set SOURCEMAP_FILE in the React Native bundle build phase and preserve the final main.jsbundle and corresponding map from that archive. Follow React Native's release debugging guide for your framework version.
For EAS Build, preserve and upload the outputs inside the build job after native compilation. Starting a remote build does not put its output files on the local computer.
For EAS Update, export once with maps:
npx expo export --platform all --source-maps --output-dir distSelect each platform's exact bundle/map pair from the export metadata. Preserve that same export when publishing; regenerating it may change the bytes. See the full Expo and EAS artifact guide for the release-owner workflow.
Build the preview CLI
From a reviewed SDK source checkout with Node.js 22 or newer:
cd packages/source-maps
npm ci
npm run buildThe command below is run from that directory. Provide the paths and release identity from your own build job. First validate without uploading:
node dist/cli.js upload --dry-run \
--url "$CRUMB_UPLOAD_ORIGIN" \
--platform android \
--app-version "$APP_VERSION" \
--native-build "$NATIVE_BUILD" \
--bundle-version "$JS_BUNDLE_VERSION" \
--bundle "$ANDROID_FINAL_BUNDLE" \
--source-map "$ANDROID_FINAL_MAP"Run separately for iOS using its identity and files. Each artifact is limited to 25 MiB.
Authenticate and upload
Obtain a project-scoped source-map upload token from your workspace owner for the configured upload service. If one has not been issued, finish the dry-run checks before arranging access. Supply it to the build job as CRUMB_SOURCE_MAP_TOKEN, through your CI secret store, or use the CLI's documented secret-file input. Remove --dry-run to upload after validation.
Never put this token in the app, an EXPO_PUBLIC_* variable, a source file, or a command-line argument. It is separate from the embedded SDK write key. Do not publish bundles or source maps alongside the documentation.
The CLI computes bundle and map hashes. Exact repeated uploads are idempotent; changed bytes with the same identity conflict. Matching hashes cannot prove that a map describes the bundle: verify a known crash location.
If the source location is missing
- Missing map: upload the pair for the report's exact identity.
- Identity mismatch or conflict: compare the app's actual metadata and archived build outputs. Do not relabel another release's map to force a match.
- Invalid artifact: use the final generated map and correct bundle; inspect the CLI's validation result.
- Raw frames remain: verify symbolication status and a known source line in the report. Native frames do not become JavaScript source locations through this upload.
The original failure remains useful even when mapping is unavailable. Crumb should preserve the raw stack and show the missing or failed mapping state rather than guess from a neighboring release.