Skip to content

generateSentryProguardUuid deadlock with Kotlin 2.3 / Gradle 9.4.1 #1129

@brentwatson

Description

@brentwatson

Gradle Version

9.4.1

AGP Version

8.13.2

Code Minifier/Optimizer

R8

Version

6.3.0

Sentry SDK Version

8.34.1

Steps to Reproduce

The build fails because of a Gradle 9 task execution engine bug involving composite finalizer groups.

Sentry Gradle Plugin creates a task dependency structure that triggers this bug.

The generateSentryProguardUuid<variant> task has zero dependencies but cannot start because Gradle placed it in a composite finalizer group — it simultaneously belongs to two finalizer chains:

  • APK listing chain: createDebugApkListingFileRedirectpackageDebugcompressDebugAssetsinjectSentryDebugMetaPropertiesgenerateSentryProguardUuidDebug
  • Sentry upload chain: uploadSentryProguardMappingsDebuggenerateSentryProguardUuidDebug + mergeDebugComposeMapping

Gradle 9's execution planner sees generateSentryProguardUuidDebug in both finalizer groups and enters a state where it can't determine which group to activate first. All workers stop. The build deadlocks.

Results in a build error that looks something like this (when assembling with R8):

Unable to make progress running work. The following items are queued for execution but none of them can be started:
  - Build ':':
      - Waiting for nodes:
          - :demo-app:uploadSentryProguardMappingsDebug (state=SHOULD_RUN, dependencies=NOT_COMPLETE, group=finalizer :demo-app:uploadSentryProguardMappingsDebug ordinal: task group 0, delegate: default group, dependencies=[:demo-app:generateSentryProguardUuidDebug (SHOULD_RUN), :demo-app:mergeDebugComposeMapping (SHOULD_RUN)], waiting-for=[:demo-app:mergeDebugComposeMapping (SHOULD_RUN), :demo-app:generateSentryProguardUuidDebug (SHOULD_RUN)], has-failed-dependency=false, finalizes=[:demo-app:assembleDebug (SHOULD_RUN)] )
          - :demo-app:assembleDebug (state=SHOULD_RUN, dependencies=NOT_COMPLETE, group=task group 0, dependencies=[:demo-app:mergeDebugNativeDebugMetadata (EXECUTED), :demo-app:packageDebug (SHOULD_RUN)], waiting-for=[:demo-app:packageDebug (SHOULD_RUN)], has-failed-dependency=false )
          - :demo-app:createDebugApkListingFileRedirect (state=SHOULD_RUN, dependencies=NOT_COMPLETE, group=finalizer :demo-app:createDebugApkListingFileRedirect ordinal: task group 0, delegate: default group, dependencies=[:demo-app:packageDebug (SHOULD_RUN)], waiting-for=[:demo-app:packageDebug (SHOULD_RUN)], has-failed-dependency=false, finalizes=[:demo-app:packageDebug (SHOULD_RUN)] )
      - Reachable nodes:
          - :demo-app:mergeDebugComposeMapping (state=SHOULD_RUN, dependencies=NOT_COMPLETE, group=finalizer :demo-app:uploadSentryProguardMappingsDebug ordinal: task group 0, delegate: default group, dependencies=[:demo-app:minifyDebugWithR8 (EXECUTED), :demo-app:produceDebugComposeMapping (EXECUTED)], has-failed-dependency=false )
          - :demo-app:generateSentryProguardUuidDebug (state=SHOULD_RUN, dependencies=NOT_COMPLETE, group=finalizer :demo-app:generateSentryProguardUuidDebug ordinal: task group 0, delegate: composite group, entry point: true, ordinal: task group 0, groups: [finalizer :demo-app:createDebugApkListingFileRedirect ordinal: task group 0, delegate: default group, finalizer :demo-app:uploadSentryProguardMappingsDebug ordinal: task group 0, delegate: default group], no dependencies, finalizes=[:demo-app:mergeDebugComposeMapping (SHOULD_RUN)] )
          - :demo-app:injectSentryDebugMetaPropertiesIntoAssetsDebug (state=SHOULD_RUN, dependencies=NOT_COMPLETE, group=composite group, entry point: true, ordinal: task group 0, groups: [finalizer :demo-app:createDebugApkListingFileRedirect ordinal: task group 0, delegate: default group], dependencies=[:demo-app:generateSentryProguardUuidDebug (SHOULD_RUN), :demo-app:mergeDebugAssets (EXECUTED)], waiting-for=[:demo-app:generateSentryProguardUuidDebug (SHOULD_RUN)], has-failed-dependency=false )
          - :demo-app:compressDebugAssets (state=SHOULD_RUN, dependencies=NOT_COMPLETE, group=composite group, entry point: true, ordinal: task group 0, groups: [finalizer :demo-app:createDebugApkListingFileRedirect ordinal: task group 0, delegate: default group], dependencies=[:demo-app:injectSentryDebugMetaPropertiesIntoAssetsDebug (SHOULD_RUN), :demo-app:preDebugBuild (EXECUTED)], waiting-for=[:demo-app:injectSentryDebugMetaPropertiesIntoAssetsDebug (SHOULD_RUN)], has-failed-dependency=false )
          - :demo-app:packageDebug (state=SHOULD_RUN, dependencies=NOT_COMPLETE, group=composite group, entry point: true, ordinal: task group 0, groups: [finalizer :demo-app:createDebugApkListingFileRedirect ordinal: task group 0, delegate: default group], dependencies=[:demo-app:compileDebugJavaWithJavac (EXECUTED), :demo-app:compressDebugAssets (SHOULD_RUN), :demo-app:l8DexDesugarLibDebug (EXECUTED), :demo-app:mergeDebugAssets (EXECUTED), :demo-app:minifyDebugWithR8 (EXECUTED), :demo-app:preDebugBuild (EXECUTED), :demo-app:processDebugManifestForPackage (EXECUTED), :demo-app:processDebugResources (EXECUTED), :demo-app:stripDebugDebugSymbols (EXECUTED), :demo-app:validateSigningDebug (EXECUTED), :demo-app:writeDebugAppMetadata (EXECUTED), :demo-app:writeDebugSigningConfigVersions (EXECUTED)], waiting-for=[:demo-app:compressDebugAssets (SHOULD_RUN)], has-failed-dependency=false )
      - Ordinal groups:
          - group 0 entry nodes: [:demo-app:assembleDebug (SHOULD_RUN)]
  - Workers waiting for work: 10
  - Stopped workers: 8

Tried with Gradle 9.2.1 and also latest version 9.4.1; same error.

We also found a temporary work around:

    // Workaround for Gradle 9.x task scheduler deadlock with Sentry plugin + Kotlin 2.3.
    // Kotlin 2.3's compose compiler introduces mergeXxxComposeMapping tasks that transform
    // the OBFUSCATION_MAPPING_FILE artifact. The Sentry plugin's toListenTo API (AGP 8.3+)
    // registers generateSentryProguardUuid as a finalizer of the compose mapping merge task.
    // This finalizer relationship, combined with the inject-assets dependency chain and
    // AGP's own finalizer groups, creates an unresolvable composite group scheduling deadlock.
    // Fix: replace the finalizer relationship with a regular dependsOn, preserving execution
    // order without causing cross-group scheduling conflicts.
    afterEvaluate {
      android.applicationVariants.configureEach { variant ->
        def variantName = variant.name.capitalize()
        def mergeTask = tasks.findByName("merge${variantName}ComposeMapping")
        def uuidTask = tasks.findByName("generateSentryProguardUuid${variantName}")
        if (mergeTask != null && uuidTask != null) {
          mergeTask.setFinalizedBy([])
          uuidTask.dependsOn(mergeTask)
        }
      }
    }

Expected Result

Should not deadlock and fail build

Actual Result

Builds fail

Metadata

Metadata

Assignees

No one assigned
    No fields configured for issues without a type.

    Projects

    Status

    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions