Your wheel can be a valid zip and still break installers. I built a checker for that.
A 5.8 GiB PyTorch ROCm nightly wheel failed to install under uv with zip64 extended information field was too long (astral-sh/uv#19440). Separately, wheel tags in wheel 0.47.0 was silently writing illegal ZIP64 structures into retagged wheels (pypa/wheel#692).
Both are the same class of problem: the ZIP64 extra field (0x0001) in the file doesn't match what APPNOTE 4.5.3 requires. Python's zipfile reads these files fine — it silently tolerates the malformation — and then a stricter installer falls over.
What zipgate checks
I parse local file headers and central directory entries by hand (not trusting zipfile) and validate:
-
SPURIOUS_ZIP64_LOCAL — a local header carries ZIP64 extra bytes for size fields that aren't
0xFFFFFFFF. This is exactly what wheel 0.47.0 did: an 8-byte ZIP64 extra in headers whose 32-bit sizes were perfectly fine. - ZIP64_LENGTH_MISMATCH — the extra field is longer or shorter than the maxed-out fields require. The uv#19440 "too long" class.
-
SIZE_MISMATCH / MISSING_ZIP64 — central directory and local header disagree, or a
0xFFFFFFFFsize has no ZIP64 backing.
It catches the real bug
I reproduced the wheel#692 MRE (lowering zipfile.ZIP64_LIMIT to force the ZIP64 path on small files, as the issue suggests), built a wheel, and retagged it:
| artifact | zipgate verdict |
|---|---|
| original wheel | VALID |
| retagged with wheel 0.47.0 |
INVALID — SPURIOUS_ZIP64_LOCAL on two entries |
| retagged with wheel 0.48.0 | VALID |
The 0.47.0 output trips the exact rule the issue describes; 0.48.0 is clean.
Use
pip install zipgate
zipgate dist/*.whl
Exit 0 = all valid, 1 = at least one invalid, 2 = unreadable. Eight tests, byte-level fixtures for both incident classes.
Repo: hahahahahahahahah6/zipgate (MIT).
If you ship wheels — especially big ones, or retagged ones — it's worth one command before upload.
Top comments (0)