🔴 The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
// DOC-DERIVED — single line, no variance; companion messages differ by tool:
java.util.zip.ZipException: zip END header not found
at java.base/java.util.zip.ZipFile.findEND(ZipFile.java:533)
at java.base/java.util.zip.ZipFile.open(ZipFile.java:613)
# same root cause in other tools
Error: Unable to initialize main class ... Caused by: java.util.zip.ZipException
Gradle: Could not expand ZIP 'guava-33.0.jar'. > zip END header not found⚡ Quick Fix Works 80% of the time
Delete the corrupted artifact from the local cache and re-resolve with checksum verification — the file on disk is simply not a valid zip.
# Gradle
rm -rf ~/.gradle/caches/modules-2/files-2.1/com.google.guava && ./gradlew --refresh-dependencies
# Maven
rm -rf ~/.m2/repository/com/google/guava/guava && mvn -U clean package🧠 Why this Happens
Tap to expand the deep technical explanation
A valid zip ends with an END-of-central-directory record — 22+ bytes whose signature the ZipFile scanner searches for from the tail backwards. When a download is cut short by network failure, when a captive portal returns its HTML login page in place of the binary, or when disk-full truncation strikes mid-write, that record never lands and the scan fails before any class is read. The JVM never even reaches your code; this is packaging-level rejection during jar opening.
The HITEC City Parking Spot Analogy:
You order a book online; the courier delivers only the first half. Flipping to the index to find chapters is impossible — there is no back cover, let alone an index.
🔁 How to Reproduce Confirm this is your error
Take any healthy jar, truncate it with head -c 5000, place it on the classpath under the expected name, and run anything that loads from it. DOC-DERIVED — deterministic single-line failure.
🛠️ Solutions (5 Ways to Fix)
Purge the cached copy and re-download with checksums on
👉 Use this first — in nine out of ten cases the build cache holds the corpse.
Build tools keep artifacts forever once fetched; deleting just the offending group directory forces re-resolution while preserving everything else. Re-run with checksum policies enabled so silent corruption cannot recur.
# locate + purge + refetch
ls -la ~/.gradle/caches/modules-2/files-2.1/com.google.guava/
rm -rf ~/.gradle/caches/modules-2/files-2.1/com.google.guava
./gradlew build --refresh-dependencies
# Maven equivalent with strict checksums (fail on mismatch)
mvn clean package -U -Dmaven.wagon.httpconnectionManager.ttlSeconds=25Identify exactly which jar is rotten with a loop validation pass
👉 Use this when the stack trace does not name the file or many jars live in lib/.
Opening every candidate with jar tf (or unzip -t) finds the broken one mechanically instead of guessing from partial logs.
for j in lib/*.jar; do
jar tf "$j" > /dev/null 2>&1 || echo "CORRUPT: $j"
doneCheck the download path: proxies, VPN portals, mirrors
👉 Use this when corruption returns after every re-download.
Corporate proxies and captive portals substitute HTML for binaries; the saved file starts with "<html" instead of PK. Bypassing the proxy or whitelisting repo hosts stops the substitution at the source.
head -c 100 broken.jar | xxd | head -3
# 3c 68 74 6d 6c ... = "<html" -> you received a web page, not a jarVerify integrity end-to-end in CI
👉 Use this to make recurrence impossible across machines and images.
Enable strict checksums so a tampered or truncated artifact fails resolution loudly instead of landing in the local cache. The CLI flag covers one build; the repository policy makes it permanent for every developer and CI runner.
# CLI - fail the build on any checksum mismatch
mvn -C clean package
# pom.xml / settings.xml - enforce per repository, permanently
<releases>
<enabled>true</enabled>
<checksumPolicy>fail</checksumPolicy>
</releases>Extract manually to confirm and salvage classes
👉 DEV ONLY — forensic use only.
DEV ONLY. unzip tolerates missing central directories far better than the JVM and can sometimes recover entries from intact local headers. Useful for a one-off rescue of a vendor jar nobody can re-fetch; never rebuild shipping artifacts from salvaged output.
# DEV ONLY - forensics
zip -FF broken.jar --out repaired.jar && unzip -l repaired.jar | head📋 Version Notes
Same exception; message text identical since forever.
Unchanged.
findEND internals reworked but public wording identical.
Unchanged; multi-release jars still require intact central directory.
🛡️ How to Prevent This Next Time
Strict checksum enforcement in builds, stable internal mirrors behind authenticated hosts, disk-space monitoring on build agents, and a CI step that validates every jar entering a release image.