Skip to content

R8 mappings

A release build minified with R8 renames your classes and methods and drops source file names. A crash from a user’s device then reads at a.b.c(SourceFile:12), which says nothing. R8 writes mapping.txt next to the build, under app/build/outputs/mapping/<variant>/; it maps every obfuscated frame back to your code: at com.example.shop.cart.Pricing.applyCoupon(Pricing.kt:42).

Upload the mapping of each release to FuseScope and the Errors tab shows Retraced stack traces. It is the Android counterpart of source maps.

  • Mappings stay private. They are stored on the FuseScope server; never ship them in the app.
  • Retracing happens when you read an error. The stored events keep their raw stack, so uploading a mapping later also fixes errors you already received.
  • Grouping uses the retraced frames. Until a release’s mapping is uploaded, its errors group on obfuscated names; see How errors are grouped.
  • You need a service account key with the Editor role, not the application’s public key: the public key can only send data.

The upload’s release must equal the SDK’s release. The SDK sends release as service.version on every event, and FuseScope looks up the mapping by that value. Both default to the variant’s versionName, so leaving both unset is the simplest way to get it right. With any other value the stack stays obfuscated.

A release keeps one mapping per build, keyed by its map id: the pg_map_id R8 writes in the mapping’s header. The plugin sends it as map_id; otherwise FuseScope reads it from the header. Uploading the same build again replaces its mapping, so variants that share a versionName, such as release and staging, keep their own.

R8 names the source file of each obfuscated frame r8-map-id-<id>. FuseScope retraces a stack with the mapping whose map id matches it, else with the release’s latest upload.

  1. Create a key. In Settings → Service accounts, create a service account with the Editor role, such as ci-android, and add a key to it. See Service accounts. Keep the key in your CI’s secrets.

  2. Apply the plugin in the app module, from Maven Central (add mavenCentral() to pluginManagement.repositories in settings.gradle.kts):

    app/build.gradle.kts
    plugins {
    id("com.android.application")
    id("io.fuseone.fusescope.android.mappings") version "0.1.0-beta.2"
    }
    fusescope {
    endpoint.set("https://fusescope.example.com")
    applicationId.set("01a16431-12ca-7064-bae9-6ac64cf20a29")
    // the API key comes from FUSESCOPE_API_KEY
    }

    The application’s R8 mappings tab has this block filled in.

  3. Build with the key in the environment:

    Terminal window
    export FUSESCOPE_API_KEY=… # Editor service account key
    ./gradlew :app:bundleRelease
    > Task :app:uploadFuseScopeMappingRelease
    FuseScope: uploaded the R8 mapping of release for release 1.8.0
  4. Ship the build, then open an error of that release: its stack shows Retraced.

The plugin adds an uploadFuseScopeMapping<Variant> task for every variant with isMinifyEnabled = true, and runs it after assemble<Variant> and bundle<Variant>. ./gradlew :app:uploadFuseScopeMappingRelease uploads on its own, from the last build.

Setting Default
endpoint Gradle property fusescope.endpoint, else FUSESCOPE_ENDPOINT The FuseScope base URL.
applicationId fusescope.applicationId, else FUSESCOPE_APPLICATION_ID The RUM application id.
apiKey fusescope.apiKey, else FUSESCOPE_API_KEY An Editor service account key, sent as SigNoz-Api-Key. Never the public rum_pub_… key.
release the variant’s versionName The release the mapping belongs to. Set it only if you also set the SDK’s release.
enabled true false skips every upload.
failOnError true Fail the build when the upload fails; false logs a warning instead.
uploadPath /api/v1/rum/applications/{applicationId}/mappings The upload path under endpoint.

When the endpoint, the application id or the key is missing, the upload is skipped with a warning, and the build succeeds:

FuseScope: mapping upload for release skipped, set apiKey in fusescope { }

So local release builds work without a key, and only CI, which has it, uploads.

Without the plugin, such as from a CI step after the build, POST the mapping to the application:

Terminal window
export FUSESCOPE_URL=https://fusescope.example.com
export FUSESCOPE_API_KEY=… # Editor service account key
curl -X POST "$FUSESCOPE_URL/api/v1/rum/applications/01a16431-12ca-7064-bae9-6ac64cf20a29/mappings?release=1.8.0" \
-H "SigNoz-Api-Key: $FUSESCOPE_API_KEY" \
-H "Content-Type: text/plain" \
--data-binary @app/build/outputs/mapping/release/mapping.txt
  • The body is the raw mapping.txt, with any content type except multipart. Add Content-Encoding: gzip to send it compressed.

  • map_id is optional, such as ?release=1.8.0&map_id=<pg_map_id>. Without it, FuseScope reads the map id from the mapping’s header; a given one must match it, or the upload fails with 400.

  • Or send multipart/form-data, with the mapping in a part named file, and release and map_id as fields before it or in the query:

    Terminal window
    curl -X POST "$FUSESCOPE_URL/api/v1/rum/applications/01a16431-12ca-7064-bae9-6ac64cf20a29/mappings?release=1.8.0" \
    -H "SigNoz-Api-Key: $FUSESCOPE_API_KEY" \
    -F "map_id=<pg_map_id>" \
    -F "file=@app/build/outputs/mapping/release/mapping.txt"
  • A 2xx answer means the mapping is stored. 400 means the body is empty, not a mapping or over the size limit; 413 that the application’s quota is full.

Open an Android application’s Settings, then R8 mappings. It is the Source maps tab of a web application, adapted to Android. Any role can see it; Admin and Editor can upload, delete and change retention.

  • Uploaded releases: one row per release with mappings, newest first, with its size, who uploaded it and when, and when it was last used to retrace an error. Expand a release to list its mappings, one per build. Delete removes them; the events are kept and their stacks become obfuscated again.
  • Upload: the Gradle plugin settings or the API curl call, with this FuseScope’s URL and the application id filled in. There is no drag-and-drop upload for mappings.
  • Retention: the same settings as for source maps: delete mappings not used for 30 days, 90 days (the default), 1 year or never, and always keep the 3 newest releases. See Retention.

Mappings and source maps share one store, so the same API lists and deletes them: GET and DELETE on /api/v1/rum/applications/{id}/sourcemaps.

Limit Default
Size of one mapping, after decompression 50 MB
Mappings per release 1 per map id (an upload of the same build replaces it)
Mappings and source maps stored per application 2 GB

Self-hosted, these are the rum.sourcemaps settings of the configuration file; see Source maps.

An Android error group whose release has no mapping shows R8 mapping missing in the Errors tab. Its detail reads Obfuscated · no R8 mapping for 1.8.0 and, under R8 mapping missing for release 1.8.0, the curl command to upload that release’s mapping.txt, with this FuseScope’s URL, the application id and the release filled in, and a link to the R8 mappings settings.

What you see Check
no R8 mapping for 1.8.0 No mapping is uploaded for release 1.8.0. Compare the release in the error header with the upload’s: 1.8.0 and 1.8.0-debug are different.
the mapping resolves none of these frames The release has mappings, but not the one of the build you shipped, so its latest was used: the app was rebuilt after the upload, or the server is older than 0.156.0 and two variants share the versionName. Upload the mapping of the build you shipped.
Some frames retraced, others not Frames of libraries not minified with your app, or classes kept by -keep rules, keep their names. Normal.
The upload task is skipped The variant isn’t minified (isMinifyEnabled = false), or the endpoint, application id or key is missing; read the Gradle warning.
The upload fails with 401 or 403 The key is wrong, or its service account is not Editor.