Everframe can process three kinds of private build artifact:
| Runtime | Report identity | Upload command |
|---|---|---|
| React and Web | context.app.build from appBuild | sourcemaps upload |
| React Native with Hermes | payload.crash.jsBundle from jsBundle | sourcemaps upload-hermes |
| Android JVM with R8 | payload.crash.jvm.mappingId from r8MappingId | r8 upload |
The identity embedded in the shipped app must exactly match the identity passed to the uploader. Everframe does not guess from an app version, filename, or the newest upload. Existing reports without an identity cannot be tagged later.
The uploader currently lives in the private Everframe workspace. Run it from a
Everframe checkout after building @everframe/cli; there is no public npx
installation implied by these examples.
Use the right credentials
Your app uses a public SDK ingest key. It is expected to ship in browser or app code and can submit reports only:
const PUBLIC_SDK_KEY = 'evf_live_PUBLIC_EXAMPLE';
Artifact upload uses a different credential: an API token with explicit
artifacts:write scope and access to the project that owns the target app.
Keep it in trusted CI as the masked EVERFRAME_API_TOKEN environment variable.
Never put this token in an app bundle, a NEXT_PUBLIC_* variable, source code,
or a CLI argument. EVERFRAME_APP_ID is the target app UUID, not an SDK key.
: "${EVERFRAME_API_TOKEN:?Set the masked artifacts:write token}"
: "${EVERFRAME_APP_ID:?Set the target app UUID}"
export EVERFRAME_API_URL='https://api.everframe.dev/api/v1'
pnpm --filter @everframe/cli... build
Treat a nonzero uploader exit as a release failure. Repeating an upload with
the same identity and identical artifacts resumes safely. Reusing an identity
for different bytes returns 409 build_conflict; assign a new identity and
rebuild the app.
Web and React
Generate a unique build ID before compiling and pass it to both the SDK and the upload job. Build IDs are exact, nonblank strings of at most 200 UTF-16 code units:
import { EverframeProvider } from '@everframe/react';
<EverframeProvider config={{
apiKey: PUBLIC_SDK_KEY,
appVersion: '2.4.0',
appBuild: WEB_BUILD_ID,
}}>
<App />
</EverframeProvider>
For @everframe/web, set the same appBuild in init(). Then upload the exact
JavaScript and adjacent .js.map files produced by that build:
node packages/cli/dist/index.js sourcemaps upload \
--app-id "$EVERFRAME_APP_ID" \
--build "$WEB_BUILD_ID" \
--dir dist/assets \
--url-prefix https://app.example.com/assets/ \
--delete-after-upload
--url-prefix must be the exact deployed origin and path represented by
--dir, including any CDN origin or base path, and must end in /. The CLI
pairs adjacent JavaScript and map files, reports JavaScript without a map, and
rejects orphan maps or an empty upload.
--delete-after-upload removes maps only after the server confirms the build
is ready and the CLI rechecks their hashes. It never deletes JavaScript. Do not
rebuild after upload: deploy those same JavaScript bytes, and ensure no source
maps remain in any public deploy directory or CDN payload.
React Native Hermes
Give every changed JavaScript or OTA build its own identity. Android and iOS
must use distinct IDs even when they share an app version. Build IDs use the
same nonblank 200-unit limit as web builds; bundle names must match
[A-Za-z0-9][A-Za-z0-9._-]{0,127}:
import { EverframeProvider } from '@everframe/react-native';
<EverframeProvider config={{
apiKey: PUBLIC_SDK_KEY,
jsBundle: {
buildId: HERMES_BUILD_ID,
bundleName: 'index.android.bundle',
},
}}>
<App />
</EverframeProvider>
Upload the final Hermes bytecode and the final composed Metro/Hermes map from the same release build. A Metro-only intermediate map cannot translate Hermes bytecode offsets.
node packages/cli/dist/index.js sourcemaps upload-hermes \
--app-id "$EVERFRAME_APP_ID" \
--build "$HERMES_BUILD_ID" \
--platform android \
--bundle-name index.android.bundle \
--bundle android/app/build/generated/assets/react/release/index.android.bundle \
--source-map android/app/build/generated/sourcemaps/react/release/index.android.bundle.map
For iOS, use --platform ios, the actual bundle name (commonly
main.jsbundle), the final HBC inside the built app resources, and the explicit
composed map written to SOURCEMAP_FILE. The Hermes uploader preserves both
input files and rejects --delete-after-upload; exclude the map from the app,
OTA package, and public artifacts yourself.
The jsBundle configuration and handled-error details described here are from
the current source tree. A JS-only update cannot add missing native bridge
support; rebuild the host app with matching JS and native SDK components.
Android R8
Generate the mapping ID before compiling the optimized build, embed it in
EverframeConfig, and retain that build’s final mapping.txt:
import dev.everframe.config.EverframeConfig
EverframeConfig(
appId = "evf_app_PUBLIC_EXAMPLE",
sdkKey = "evf_live_PUBLIC_EXAMPLE",
r8MappingId = BuildConfig.EVERFRAME_R8_MAPPING_ID,
)
Upload the mapping from trusted CI:
node packages/cli/dist/index.js r8 upload \
--app-id "$EVERFRAME_APP_ID" \
--mapping-id "$EVERFRAME_R8_MAPPING_ID" \
--mapping app/build/outputs/mapping/release/mapping.txt
The mapping ID must match the optimized binary and the pattern
[A-Za-z0-9][A-Za-z0-9._-]{0,127}. Everframe retraces Java and Kotlin JVM frames
only. Android NDK crashes and ELF symbols are not collected or processed by
this workflow.
Enable processing on the API
Artifact upload and frame processing are separate. Apply the API migrations,
then deploy the entire API dist output together, including
dist/processor-child.js and its shared chunks. Set this on the API instance
and restart it:
SOURCE_MAP_WORKER_ENABLED=true
The worker is disabled by default. For R8, provision Java 17 or newer and the
pinned R8 9.4.17 runtime, then also set R8_WORKER_ENABLED=true; that flag does
not start the shared worker by itself. Web and Hermes processing need only the
shared flag.
Reports can arrive before their artifacts or while processing is disabled. Once the exact upload is ready, the worker can enrich retained reports without changing the raw envelope, report ID, client fingerprint, occurrence count, or webhook history.
One late-upload recovery path remains unfinished: a completed processor-v1 result is not reopened solely because maps for retained generic cause frames arrive later. Do not treat the current branch as end-to-end completion of generic cause mapping or its optimized dashboard presentation.
Read and troubleshoot results
The error detail stack opens in Mapped mode when mapped web or Hermes frames are available, or Retraced mode for R8 output. Choose Raw at any time to see the frames captured by the SDK. A partial result can mix mapped and raw frames; Everframe never substitutes a nearby build’s artifact.
For Web and React, maps can also support opt-in server grouping and release-aware regression handling. Those controls, their additional worker gates, and their limits are covered in Error triage & releases.
| Result | What to check |
|---|---|
missing_build or missing_js_bundle | Embed the exact web appBuild or Hermes jsBundle identity before compiling. Untagged reports cannot infer it later. |
missing_artifacts | Confirm the token’s project access, target app UUID, exact build or mapping ID, upload completion, and retention. |
Web missing_artifact or no_mapping | Compare the full captured frame URL with --url-prefix, including CDN origin and base path, and confirm the throw-site map was emitted. |
Hermes no_mapping | Confirm the uploaded bytecode and map are the final composed pair from the shipped build. |
409 build_conflict or expired identity | Use a fresh ID and rebuild. Do not reuse an ID for changed bytes. |
| Pending indefinitely | Check migrations, SOURCE_MAP_WORKER_ENABLED=true, the deployed dist/processor-child.js, private blob availability, and worker logs. |
| R8 stays raw | Also check R8_WORKER_ENABLED=true and the server’s Java/R8 runtime. |
401 or 403 | Use a non-revoked API token with artifacts:write and project access, never the public SDK key. |
503 processing_limit | Let the CLI’s bounded retry honor Retry-After, or retry after processor capacity is available. |
invalid_map or another failed result | Check artifact format and limits. Raw frames remain available. |
Current limits are 32 MiB per map, 256 MiB per build, 500 artifacts per web build, and 1 GiB of stored or reserved artifacts per project. Ready artifacts expire after 90 days; incomplete uploads expire after 24 hours, and expired identities cannot be reused.
This workflow does not process Apple dSYMs, native iOS crashes, Android NDK crashes, or ELF symbols. Keep the platform crash and symbol files required by your other crash tooling.