Skip to content

Crashlytics mapping-file-id resource silently dropped under AGP 9.3.1 #8564

Description

@deepakadvait

Crashlytics mapping-file-id resource silently dropped under AGP 9.3.1

Summary

Under AGP 9.3.1, the Crashlytics Gradle plugin's InjectMappingFileIdTask generates a valid, correctly-formed resource file containing the mapping-file ID (com.google.firebase.crashlytics.mapping_file_id), and registers it via the documented Variant API (variant.sources.res?.addGeneratedSourceDirectory(...)). The registration call completes without error, and AGP's own resource-merge validator recognizes the resource as a declared entry (it is caught in duplicate-resource conflict detection when a competing declaration is added). However, the resource never appears in the final compiled resources.arsc / packaged APK. Crashlytics is therefore unable to associate crash reports with the correct build/mapping file, and no crashes are received in the Firebase console for any release built this way — despite Play Console's own separate crash/ANR vitals confirming the app is genuinely crashing in production.

This worked correctly on AGP 8.13.2 with the same Crashlytics Gradle plugin version and stopped working immediately after upgrading to AGP 9.3.1.

Environment

  • AGP: 9.3.1 (regression from working AGP 8.13.2)
  • Crashlytics Gradle plugin: reproduced on both 3.0.4 and 3.0.7 (latest available at time of testing)
  • Gradle: 9.5.0
  • google-services plugin: 4.4.3
  • Firebase Crashlytics SDK: firebase-crashlytics-ktx 18.1.0
  • Kotlin: 2.3.20

Steps to reproduce

  1. Configure a release build type with isMinifyEnabled = true and the Crashlytics Gradle plugin applied.
  2. Build the release variant with AGP 9.3.1 (./gradlew assembleRelease).
  3. Inspect the generated task output at build/generated/res/injectCrashlyticsMappingFileId<Variant>/values/com_google_firebase_crashlytics_mappingfileid.xml — this file is present and correctly formed with a valid UUID.
  4. Inspect the final packaged APK's resource table (e.g. aapt2 dump resources app-release.apk, or strings on the extracted resources.arsc) for com.google.firebase.crashlytics.mapping_file_idit is absent.
  5. Repeat steps 1–4 on AGP 8.13.2 with the same Crashlytics plugin version — the resource is present and correct.

What we verified while diagnosing this

  • The task itself (injectCrashlyticsMappingFileId<Variant>) runs successfully and produces a correct output file every time — this rules out a generation-side bug.
  • variant.sources.res is non-null at the point the plugin (and our own replicated call) invokes addGeneratedSourceDirectory — rules out the null-safe-call-skips-registration theory.
  • AGP's own android_res_source_set_path_map intermediate file lists the generated directory as a known, registered resource source for the variant — so the directory is not being ignored at the source-set level.
  • The resource is absent from every intermediate compiled .flat file in merged_res/<variant>/merge<Variant>Resources/, not just the final packaged output — so it is dropped before or during compilation, not at a later packaging step.
  • Independently re-implementing the exact same wiring the plugin uses (variant.sources.res?.addGeneratedSourceDirectory(task, Task::resourceDir), called directly from the app's own build.gradle.kts against the plugin's own InjectMappingFileIdTask type) reproduces the same silent failure — this is not specific to how the plugin itself is written, and confirms it's not fixable purely from the plugin's side without an AGP-level fix, or not fixable from consuming build scripts either.
  • AGP's own native resValue(...) mechanism (an unrelated, hand-declared static string resource already used elsewhere in this project's debug build type) works correctly under the exact same AGP 9.3.1 setup — ruling out a general "AGP 9.3.1 can't generate resources at all" explanation. The issue is specific to resources registered via addGeneratedSourceDirectory in this particular code path/timing, not resource generation in general.
  • Attempting to work around this by supplying the same resource value through variant.resValues.put(...) (a mechanism confirmed working in this project) fails with a duplicate-resource merge error against the original, non-functional Crashlytics-generated declaration — i.e. AGP's resource merger still "sees" the broken declaration as a real, conflicting entry during validation, even though that same declaration never survives into the actual compiled output. This inconsistency between what the merger validates against and what it actually includes is itself worth flagging as part of the bug.
  • This is not the known isShrinkResources issue where an unreferenced resource gets stripped by the resource shrinker (see Crashes when isShrinkResources is set to true, it reports "The Crashlytics build ID is missing..." #5562) — we tested explicitly protecting the resource via a tools:keep entry in res/raw/keep.xml, and it made no difference. This is consistent with the resource being dropped before the merge/compile stage, well before shrinking would ever run on it.
  • Also tested migrating the release build type to AGP 9.3+'s new unified optimization { enable = true } DSL (which changes how R8 handles code and resource optimization together) in place of the legacy isMinifyEnabled/isShrinkResources/proguardFiles DSL — no difference; the resource is still absent from the final compiled resource table either way.

Expected behavior

The mapping-file-id resource generated by InjectMappingFileIdTask should be included in the final compiled resource table under AGP 9.3.1, exactly as it is under AGP 8.13.2, so that Crashlytics can correctly tag and de-obfuscate crash reports.

Actual behavior

The resource is generated correctly, registered via the documented API without error, and recognized by AGP's resource-merge validator — but is absent from every compiled intermediate and the final packaged resource table. As a direct consequence, Firebase Crashlytics receives zero crash reports for any release built with this AGP version, even though the app is confirmed to be crashing in production (verified via Google Play Console's separate Android vitals, which uses a different, non-Firebase-SDK-based reporting path).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions