Gotchas
Archivey's one interface hides a lot of format history. Defaults stay on the cheap, honest path — but some traps are still format law, stdlib behaviour, or upstream native code. If you read only one page after Reading members, make it this one.
This page is a digest: one line per trap, and a link to the page that owns the detail. A topic is here only if a caller choice is likely to shoot you in the foot, or if Archivey cannot fully deliver on failing loudly and verifying. Format matrices, policy tables and unsupported-feature lists live on their owning pages.
What you should and shouldn't do
- Don't seek backwards in a compressed member without meaning it. Without
seekable_members=True,seek()raises. With it and no index or accelerator, a backward seek re-decompresses from the start — loudly, viaSTREAM_REWIND_REDECOMPRESSES, but it still costs. → Seeking - Don't expect
seek()on a WinZip AES or encrypted 7z member yet.seekable_members=Trueis a guarantee on randomopen()everywhere else; those two decrypt wrappers still raise. → Seeking - Don't open members out of order in a solid archive. On solid 7z / RAR and any
compressed TAR, a named
open()can restart the whole block. Prefer one forward pass.concurrent_members=Truemakes overlapping streams correct; it does not make them cheap. → Solid archives - Don't expect a second pass in streaming mode. The first of
__iter__/stream_members/extract_allconsumes it — including after an earlybreak. → Streaming is one pass - Don't assume a name identifies one member.
get(name)is last-wins when names collide, and a name in a selector matches every member with that name —stream_members(members=["x"])hands you each version in turn. Pass anArchiveMemberwhen you mean one identity. → Duplicate names - Don't assume the file lands at
member.name. UnderSTRICT, trailing dots and spaces are stripped and non-UTF-8 bytes percent-escaped; case and Unicode-normalisation twins collide on every OS, not just Windows. → Names change on disk - Don't
read()a member from an untrusted archive without a size guard.read()is unbounded, andstream_members()is deliberately outsideListingLimits. Chunk untrusted payloads. → Limits - Don't recurse into nested archives without bounding it yourself. The bomb tracker checks expansion for individual archives and is not nesting-aware, so a zip-of-zips can amplify past your limits one level at a time. → Limits
- Don't close a source underneath a live accelerator-backed stream. Archivey contains the upstream fault and re-raises it as a normal Python error, so this is a clean failure rather than a crash — but the stream is still dead and the read still fails. → Accelerators
- Do turn accelerators off for untrusted input under a hard latency budget
(
AcceleratorMode.OFF), or enforce your own timeout: crafted input can busy-loop in C++ where a Python timeout cannot cleanly interrupt it. → Hardening notes
What you should be aware of
Places where Archivey cannot fully deliver "fail loudly and verify". None of these are bugs; all of them are stated so you can decide whether they matter to you.
- Archivey is stricter than the stdlib about damage. Where
tarfileandgzipoften stop quietly, Archivey raises or emits a diagnostic. Code ported from the stdlib may start seeing errors on archives that "worked". - A wrong 7z password can yield garbage. With AES plus store/copy and neither a
folder digest nor a member CRC, the format offers no check value — matching 7-Zip.
Archivey emits
DIGEST_UNVERIFIABLE(reason="no_integrity_anchor"). Treat the payload as unverified. → 7z - A 7z header-decryption residual remains. A wrong password that decodes to a
plausible non-empty header can still parse; an empty one is rejected as
EncryptionError, never a silent empty listing. Don't read "0 members" as proof of emptiness without checking diagnostics. - TAR has two honesty residuals. A trailer-less or
cat-joined tar is warned about, not raised — it is byte-identical to a truncation at a member boundary; setstrict_archive_eof=Truewhen you need a provably complete listing. And a corrupt final header is caught in random access but not in forward-only streaming. → TAR strict_archive_eof=Truereads to the end of the file. It requires every byte after the two-block trailer to be zero, so trailing junk and concatenated archives raise instead of passing silently. Zero padding still passes —tarwrites 10 KiB records. The cost is the point of the flag being opt-in: the check is O(tail length), and on a.tar.gzthe tail is decompressed to inspect it.- Truncation detection on bare gzip/zlib through rapidgzip is best-effort.
Upstream soft-EOFs by design and Archivey backstops it, but a residual hole
remains. Use
use_rapidgzip=OFFwhen you need certainty. This is about bare streams — ZIP/7z members carry their own CRC and fail properly. → Single-file compressors .Ztruncation is partly silent. Only nonzero leftover bits raise; a cut on a code boundary stays quiet.import archiveypatches pycdlib process-globally. A hang-safety guard is installed inside pycdlib's namespace. Other code using pycdlib in the same process sees that guarded behaviour — a strict superset of correct results on valid trees. → ISO 9660- An empty listing is a diagnostic, never an error. An empty tar is all zeros, so
no rule over the bytes can reject a zero-filled junk file without also rejecting a real
one — and not a length rule either:
tar's-bblocking factor makes every block-aligned zero length legitimate (tar -b 64writes an empty archive that is 32768 zero bytes, byte-identical to a 32 KiB junk file). Empty tars are common in practice: Docker and OCI images carry a 1024-byte one as the empty layer behind every metadata-only instruction. Archivey opens it, reports zero members, and emitsEMPTY_ARCHIVE(plusEXTENSION_FORMAT_UNCONFIRMEDwhen the format came only from the filename, orEXPLICIT_FORMAT_LISTED_EMPTYwhen you passedformat=and detection disagrees). If "0 members" would mean something is wrong for you, check the count or usedetect_format(), which does refuse zero-filled bytes — a tar'sustarmagic lives inside a member header, so an empty one has nothing to match and reaches the TAR reader only by file extension or an explicitformat=. → Errors and diagnostics - Brotli without a
.brname is identified by a content probe. When the source length is known, a framing check rejects declared lengths that cannot fit; on a non-seekable stream of unknown length the gate is skipped. A residual can still open as a single fabricated member. If nothing corroborated the probe (no matching extension, no inner-TAR upgrade), a failed read setsformat_unconfirmed=True— and may already have delivered a buffer of copied bytes. → Formats — Detection - Prefer
reader.diagnosticsand the extraction report over logs. Advisories are queryable data, not just log lines. → Errors and diagnostics