Watch at EaseStories. Answers. Perspective.

Technology · PRACTICAL GUIDE

Python BadZipFile? Separate ZIP structure, CRC and required files

A PK prefix, a successful listing and a passing integrity test answer different questions. Preserve the input before choosing a repair.

Original editorial illustration: ZIP structure, CRC and required contents are separate checks.
Original editorial illustration: ZIP structure, CRC and required contents are separate checks. Watch at Ease

When Python raises BadZipFile: File is not a zip file, retain the exact input and traceback first. The message does not establish that changing the filename, deleting a cache or removing bytes will recover the intended archive.

Use three separate gates: can Python read the archive structure, can it read the member data without detected CRC errors, and does the archive contain what this delivery requires? Our five small synthetic tests show why none should silently stand in for the next.

1. Preserve the actual input and the failing stage

Record the resolved input path, byte size, SHA-256, Python version and full exception. Keep the original unchanged and work on a copy. A locally computed digest records identity; it proves authenticity only when compared with an independently trusted reference for that exact artifact.

For a downloaded file, confirm the authorized source and intended release, and examine whether the saved response is actually the expected archive rather than an error or sign-in page. For a generated ZIP, ensure the writer has closed before a reader opens it: Python documents that closing writes essential records. Do not open the original with mode w, which truncates it.

2. Inspect structure before reading every payload

On a known, suitably small candidate, use zipfile.is_zipfile(path), then with zipfile.ZipFile(path, "r") as z and inspect z.infolist(). Record names, file_size, compress_size and compress_type. If construction itself fails, there is no successful member listing to accept. PKWARE requires an end record; the first two letters alone do not establish that the directory is complete.

Only after reviewing source and resource scope, run z.testzip() inside that context. It reads member data and returns the first bad filename or None when its checks pass. Preserve any exception as an unresolved result rather than converting it into success. Declared sizes help assess scope but are not a hard resource boundary; large or untrusted archives need isolated processing with enforced limits.

3. Compare the five measured cases

We created tiny BytesIO fixtures with Python3.12.14 and performed no extraction. A good 136-byte ZIP listed report.txt and returned None from testzip(). Removing its 22-byte end record left a 114-byte sample that still began with PK, but is_zipfile() returned False and ZipFile construction raised BadZipFile.

Changing one stored payload byte without updating its CRC kept the ZIP at 136 bytes. is_zipfile() remained True and report.txt still appeared in the listing, but testzip() returned report.txt. A valid 22-byte empty ZIP returned True and None with an empty listing. A 20-byte HTML sample failed format recognition. These controlled cases illustrate distinct failure stages; they do not diagnose your file or test every ZIP feature.

4. Repair the demonstrated cause, then verify the delivery

For wrong-format or incomplete bytes, obtain a separately named copy through the normal authorized route and compare the exact artifact reference. If the producer left the ZIP unfinished, regenerate and close it properly. If the error identifies an unsupported compression or encryption feature, check the installed reader’s documented support rather than labelling the file corrupt from the exception alone; Python zipfile does not handle multipart ZIPs.

Keep a record of artifact hash, structural result, CRC result, expected member names and final application test. A passing CRC does not authenticate a publisher, reject malicious content or prove the expected files are present. If required files are missing, stop even when testzip() returns None. Avoid generic signature-truncation patches on the only original: making an archive open does not recreate missing payloads.

Original editorial illustration: outcomes of our tiny Python3.12.14 synthetic fixtures.
Original editorial illustration: outcomes of our tiny Python3.12.14 synthetic fixtures. Watch at Ease