Skip to content

sentry.gradle.kts still breaks Gradle Configuration Cache on 8.19.0 / Gradle 9 — a small, targeted fix is possible ahead of the SAGP migration (#796) #6466

Description

@fabiendem

What React Native libraries do you use?

Hermes, RN New Architecture, React Native without Frameworks

Are you using sentry.io or on-premise?

sentry.io (SaS)

Are you using any other error monitoring solution alongside Sentry?

Yes

Other Error Monitoring Solution Name

Datadog (@datadog/mobile-react-native) and Luciq (@luciq/react-native). Noted because both ship Gradle scripts with the same Configuration Cache incompatibility — this report is scoped to Sentry's.

@sentry/react-native SDK Version

8.19.0 (latest). Confirmed via a Configuration Cache build failure on 8.10.0; the code responsible is present verbatim in 8.19.0's sentry.gradle.kts (line references below), so it applies to latest — I have inspected the source but not re-run a build on 8.19.0.

How does your development environment look like?

Gradle: 9.0.0 (Configuration Cache is the recommended execution mode; Gradle prints this on every build)
AGP: 8.x
React Native: 0.83.9, Hermes enabled, newArchEnabled=true
Build: ./gradlew :app:bundleRelease (release AAB; observed on Linux CI, ubuntu-24.04)

Sentry.init()

Sentry.init({
  dsn: 'https://...@sentry.io/...',
  // standard options; the failure is at build time, independent of runtime init options
});

Steps to Reproduce

  1. Set org.gradle.configuration-cache=true in android/gradle.properties
  2. Set org.gradle.configuration-cache.problems=warn (to get past warn-level problems and surface the hard failures)
  3. ./gradlew :app:bundleRelease (or any release bundle/assemble)

Expected Result

The build succeeds with Configuration Cache enabled (stores an entry on the first run, reuses it — and skips the configuration phase — on subsequent runs).

Actual Result

problems=warn does not rescue these — they are hard execution-time exceptions (this is also what #4252 reported, with warn mode set):

* What went wrong:
Could not evaluate onlyIf predicate for task
  ':app:createBundleReleaseJsAndAssets_SentryUpload_<pkg>@<version>+<code>'.
> Could not evaluate spec for 'Task satisfies onlyIf closure'.

The same failure also occurs for :app:copySentryJsonConfiguration.


Root cause

Line references are to packages/core/sentry.gradle.kts on main (shipped in 8.19.0); the Groovy sentry.gradle in 8.10.0 has the identical constructs.

1. onlyIf predicates call closures resolved from project's extra properties, and are evaluated at execution time.

// sentry.gradle.kts
fun shouldCopySentryOptionsFile(): Boolean {
    val closure = extra["shouldCopySentryOptionsFile"] as groovy.lang.Closure<*>   // L87 — reads project state
    return closure.call() as Boolean
}

tasks.register("copySentryJsonConfiguration") {                                     // L103 (unconditional)
    onlyIf { shouldCopySentryOptionsFile() }                                        // L104 — evaluated at execution time → project access
    ...
}

Also onlyIf { shouldCopySentryOptionsFile() } at L159 and onlyIf { shouldSentryAutoUploadGeneral() } at L561 (the *_SentryUpload task). onlyIf runs at execution time; resolving the extra-property closure there is the CC violation.

2. copySentryJsonConfiguration's doLast touches project at execution time.

doLast {
    val appRoot = project.rootDir.parentFile ?: project.rootDir   // L106 — project at execution time
    ...
    copy { from(sentryOptionsFile); into(androidAssetsDir); ... }  // L113 — project.copy at execution time
}

3. *_SentryUpload uses project.logger at execution time (e.g. L594, L596, and elsewhere in its actions). The CLI invocation itself already uses the CC-safe injected ExecOperations (InjectedExecOps, defined L76, instantiated L577) — so only these residual project accesses remain.

Why the usual escape hatches don't work

  • Upgrading to the 8.14.0+ .kts rewrite (#6119) does not help — verified by reading sentry.gradle.kts on main: it is a syntax migration, the CC-relevant constructs above are unchanged.
  • Env-var gating (SENTRY_DISABLE_AUTO_UPLOAD=true, SENTRY_COPY_OPTIONS_FILE=false) does not help: copySentryJsonConfiguration is registered unconditionally (L103) and wired unconditionally via preBuild.dependsOn("copySentryJsonConfiguration") in afterEvaluate (L781–782), so it is always in the task graph and its onlyIf is always evaluated. The env var only changes the predicate's result, not whether the failing evaluation runs.

Proposed minimal fix (interim, ahead of #796)

All CC-safe patterns, no behaviour change:

  1. onlyIf (L104, L159, L561) — capture the env read at configuration time and reference the captured boolean:

    val copyOptionsFile   = System.getenv("SENTRY_COPY_OPTIONS_FILE") != "false"
    val autoUploadGeneral = System.getenv("SENTRY_DISABLE_AUTO_UPLOAD") != "true"
    // ...
    onlyIf { copyOptionsFile }
    onlyIf { autoUploadGeneral }

    (System.getenv at configuration time is a tracked CC input.)

  2. project.rootDir (L106) — capture at configuration time and use the local in doLast.

  3. copy {} (L113) — use an injected FileSystemOperations (same injection pattern already used for ExecOperations), or java.nio.file.Files.copy for the single-file case.

  4. project.logger (L594/L596/actions) — use the task's own logger instead of project.logger (the task logger is CC-safe).

I'm happy to open a PR with these changes as an interim step before the full SAGP migration in #796 — let me know if you'd prefer to fold it directly into that work instead.

References

Metadata

Metadata

Assignees

No one assigned

    Fields

    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