Making the wrapper more resilient to corrupted downloads

We’re seeing occasional failures on our CI agents when the wrapper doesn’t manage to download the zip of the Gradle distribution properly and then fails to unzip it. For example:

Unzipping /home/bamboo/.gradle/wrapper/dists/gradle-3.0-bin/1kehl3rnolmb1vuovp7uouoslb/gradle-3.0-bin.zip to /home/bamboo/.gradle/wrapper/dists/gradle-3.0-bin/1kehl3rnolmb1vuovp7uouoslb
Exception in thread "main" java.lang.RuntimeException: java.util.zip.ZipException: zip file is empty
	at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:78)
	at org.gradle.wrapper.Install.createDist(Install.java:44)
	at org.gradle.wrapper.WrapperExecutor.execute(WrapperExecutor.java:126)
	at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:55)
Caused by: java.util.zip.ZipException: zip file is empty
	at java.util.zip.ZipFile.open(Native Method)
	at java.util.zip.ZipFile.<init>(ZipFile.java:219)
	at java.util.zip.ZipFile.<init>(ZipFile.java:149)
	at java.util.zip.ZipFile.<init>(ZipFile.java:163)
	at org.gradle.wrapper.Install.unzip(Install.java:157)
	at org.gradle.wrapper.Install.access$400(Install.java:26)
	at org.gradle.wrapper.Install$1.call(Install.java:67)
	at org.gradle.wrapper.Install$1.call(Install.java:44)
	at org.gradle.wrapper.ExclusiveFileAccessManager.access(ExclusiveFileAccessManager.java:65)
	... 3 more

As you’d expect, its frequency increases following a Gradle version upgrade or when the CI agents are clean as it increases the likelihood that a download will be necessary.

I wonder if it would make sense to make the wrapper more resilient to this sort of failure? Perhaps it could try the download again if the unzip fails? Or, taking things a step further, perhaps it could also download a checksum for the zip and check that the downloaded zip’s checksum matches before attempting to unzip it?

@Andy_Wilkinson how long have you been seeing this problem. Is this a relatively recent issue?

FWIW, this is already possible. It doesn’t download the checksum, but you can provide a known valid checksum in the gradle-wrapper.properties file. It will just need to be updated when you update wrapper versions.

https://docs.gradle.org/current/userguide/gradle_wrapper.html#sec:verification

This does not attempt to retry though.

I can’t be certain, but it feels like a relatively recent issue otherwise I probably would have posted here before now. FWIW, I also think I’ve seen it with both Gradle 2.x and 3.x.

Thanks for the tip.

Unrelated to Andy, we’ve seen this since we started using Gradle (around v1.8). The failure rate averages about 1%, but there is clear correlation between failure rate and peak usage on our development network. I haven’t seen the problem while on any “reliable” network (low usage / adequate bandwidth / well connected, etc.).

However, +1 for more resiliency on the download as every Gradle upgrade normally yields a question from a couple developers that have just encountered the issue for the first time or don’t remember they need to delete the bad file to continue.

We had recently (last week) made a change which enforced HTTPS on all wrapper downloads. Turns out this didn’t work if you have a http:// URL set in your gradle-wrapper.properties file as Java doesn’t autofollow cross-protocol redirects using UrlConnection. Any chance this sounds like what you are running into? We’ve disabled this behavior but perhaps something is caching this redirect?

This seems reasonable. Right now the logic is pretty simple and optimistic. We could probably make this more robust w/o adding much bloat to the wrapper jar.

I’m mainly concerned that we didn’t introduce some flakiness in our infrastructure due to some recent changes. So it’s important for me to understand if this is recent behavior or not.

The project where I just saw this early today does have an http URL configured. However, it was running on new CI infrastructure following an outage. Caching seems unlikey although I can’t rule it out.

It would be nice if the wrapper task did this automatically.

We now publish sha256 checksums with every release. We’ve also backfilled the data for older releases.

We have a need for retries here as sometimes, due to network load/congestion, the download of the gradle wrapper pops. Is there an open ISSUE for this somewhere? If not where would one start looking to implement this feature to send in a PR?