Retry transient network failures when bootstrapping the SignalR Java Gradle distribution
Problem
SignalR Java builds can fail during Gradle distribution bootstrap on transient DNS/network errors. The checked-in Gradle 9.2.1 wrapper does not retry hostname-resolution failures, so one failed lookup aborts the Java build with MSBuild error MSB3073.
This issue concerns Gradle bootstrap only. Independent Blazor test and Windows test-log file-lock failures in the same pipeline are out of scope.
Evidence
Pipeline: aspnetcore-ci (83), build 1597302, branch refs/pull/67492/merge, associated with dotnet/aspnetcore#67492.
Executed merge commit: bee4e13f67ce8bc47fa19233dc51b3f591794e0d.
The initial Test: macOS job (job attempt 1, Run build.sh task attempt 1) failed on September 15, 2026.
Failed step · Exact failure log
L1239: 2026-09-15T10:37:48.6364680Z Downloading https://services.gradle.org/distributions/gradle-9.2.1-bin.zip
L1241: 2026-09-15T10:38:20.4837890Z Exception in thread "main" java.net.UnknownHostException: services.gradle.org
L1264: 2026-09-15T10:38:22.7538790Z ##[error]eng/targets/Java.Common.targets(59,5): error MSB3073: The command "../gradlew -Dorg.gradle.daemon=false --stacktrace --console=plain compileJava" exited with code 1.The same build's macOS job attempt 2 downloaded the same distribution and compiled Java successfully at the same source commit, on a different agent with the same macOS image version (20260824.0482.1).
L1239: 2026-09-15T12:33:17.2718480Z Downloading https://services.gradle.org/distributions/gradle-9.2.1-bin.zip
L1242: 2026-09-15T12:33:37.3875450Z Welcome to Gradle 9.2.1!
L1295: 2026-09-15T12:35:12.5305460Z > Task :core:compileJava
L1309: 2026-09-15T12:35:22.3667290Z BUILD SUCCESSFUL in 2m 4sSame-commit checkout evidence: initial checkout and recovery checkout.
This strongly supports a transient or agent-specific infrastructure failure. It does not identify whether the faulty layer was the agent resolver, proxy/network path, or upstream DNS.
Implementation context
The Gradle-provided wrapper is checked into src/SignalR/clients/java/signalr/: gradlew, gradlew.bat, gradle/wrapper/gradle-wrapper.jar, and gradle/wrapper/gradle-wrapper.properties.
- Wrapper properties at the failing commit configure Gradle 9.2.1, a SHA-256 checksum, and
networkTimeout=10000. The timeout is not a retry policy. - Java.Common.targets invokes
gradlew ... compileJavadirectly viaExec. - The checked-in wrapper JAR was verified to match the upstream Gradle 9.2.1 wrapper JAR.
- Upstream
Install.fetchDistributionretries invalid ZIP downloads by catchingZipException, but letsUnknownHostExceptionpropagate.Download.downloadInternalhas connect/read timeouts but no network retry loop.
Proposed solution
Add bounded backoff retries specifically for Gradle distribution bootstrap, before Java compilation. Prefer an existing repository bootstrap/download helper if suitable. A repository-side bootstrap helper is one option; adopting an upstream wrapper enhancement is another if it demonstrably covers DNS failures. Do not hand-edit or fork the generated wrapper JAR.
Suggested policy: three total acquisition attempts with short delays, such as 2 seconds then 5 seconds. These values are a proposal, not existing behavior.
Requirements:
- Retry only classified transient acquisition failures: hostname resolution, connection reset/timeout, and selected transient HTTP responses.
- Do not retry arbitrary
gradlewfailures, Java compilation, tests, or the fullRun build.shstep. - Use the configured distribution URL/version/checksum rather than introducing a second hard-coded configuration.
- Preserve mandatory SHA-256 verification. Checksum mismatches and non-transient errors must fail explicitly; never execute an unvalidated or partial distribution.
- Preserve wrapper cache behavior and safe concurrent acquisition. Avoid unnecessary downloads on valid cache hits and competing unsafe cache writers.
- Log the endpoint, attempt count, reason, and backoff; retain a clear terminal error after the final attempt. Do not log credentials.
- Keep behavior consistent across supported launch paths, particularly macOS.
A trusted mirror or checksum-validated pipeline cache could additionally reduce external dependency exposure, but cache misses still require correct failure handling.
Acceptance criteria
- With an empty cache, an injected transient DNS failure followed by recovery succeeds within the bounded acquisition attempts; compilation then runs once.
- Persistent DNS/network failure exhausts the attempts and fails clearly with retry history.
- Checksum mismatches and non-transient failures are not masked or retried as ordinary transient network errors.
- Valid cache hits avoid acquisition.
- Interrupted downloads and concurrent bootstrap cannot produce an accepted corrupt distribution.
- Genuine Java compilation/test failures propagate without retries.
- Existing successful SignalR Java build behavior remains intact.
No implementation or CI reruns were performed during the investigation.
Source: dotnet/aspnetcore