can1357/oh-my-pi · error · ArchiveError
Invalid ZIP archive: truncated central-directory entry
Error message
Invalid ZIP archive: truncated central-directory entry
What it means
This ArchiveError is thrown while parsing a ZIP central-directory header: the computed end offset of the entry (name + extra + comment fields) extends past the end of the directory buffer, or is not a safe integer. It means the central directory record is truncated or corrupt, so the library refuses to parse further rather than return garbage.
Source
Thrown at packages/utils/src/ar/zip.ts:478
const flags = readUInt16LE(directory, offset + 8);
const method = readUInt16LE(directory, offset + 10);
const dosTime = readUInt16LE(directory, offset + 12);
const dosDate = readUInt16LE(directory, offset + 14);
const crc = readUInt32LE(directory, offset + 16);
const compressedRaw = readUInt32LE(directory, offset + 20);
const uncompressedRaw = readUInt32LE(directory, offset + 24);
const nameLength = readUInt16LE(directory, offset + 28);
const extraLength = readUInt16LE(directory, offset + 30);
const commentLength = readUInt16LE(directory, offset + 32);
const diskStartRaw = readUInt16LE(directory, offset + 34);
const externalAttributes = readUInt32LE(directory, offset + 38);
const localOffsetRaw = readUInt32LE(directory, offset + 42);
const nameStart = offset + 46;
const extraStart = nameStart + nameLength;
const commentStart = extraStart + extraLength;
const end = commentStart + commentLength;
if (!Number.isSafeInteger(end) || end > directory.byteLength) {
throw new ArchiveError("Invalid ZIP archive: truncated central-directory entry");
}
assertArchivePathBytes(nameLength, "member path", options.limits.maxPathBytes);
const rawName = directory.subarray(nameStart, extraStart);
const extra = parseExtra(directory.subarray(extraStart, commentStart), rawName);
const values = applyZip64Values(
extra.zip64,
{
compressedSize: compressedRaw,
uncompressedSize: uncompressedRaw,
localHeaderOffset: localOffsetRaw,
diskStart: diskStartRaw,
},
{
compressedSize: compressedRaw === U32_MAX,
uncompressedSize: uncompressedRaw === U32_MAX,
localHeaderOffset: localOffsetRaw === U32_MAX,
diskStart: diskStartRaw === U16_MAX,
},View on GitHub (pinned to 9690622007)
Solutions
- Re-download or re-export the archive; verify its integrity (checksum) against the source
- Test the archive with an independent tool (unzip -t) to confirm it is corrupt rather than a library bug
- Ensure the whole file was uploaded/downloaded (compare byte sizes) before reading
- If you produce ZIPs in-house, fix the writer so central-directory name/extra/comment lengths match the actual bytes written
Example fix
// before: reading a possibly partial download
await readZip(Bun.file(url));
// after: check expected size first
const file = Bun.file(path);
if (file.size < expectedSize) throw new Error(`Incomplete download: ${file.size}/${expectedSize}`);
await readZip(file); Defensive patterns
Strategy: validation
Validate before calling
const stat = await Bun.file(zipPath).stat();
if (!stat.isFile() || stat.size === 0) throw new Error('not a usable zip file');
if (expectedSize && stat.size !== expectedSize) throw new Error(`truncated: ${stat.size}/${expectedSize}`); Try / catch
try {
const entries = await readZip(file);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes('truncated central-directory entry')) {
throw new Error(`Archive corrupt: ${file.name} — re-download required`, { cause: err });
}
throw err;
} Prevention
- Verify checksums/byte sizes of downloaded archives before reading
- Test untrusted archives with `unzip -t` in a sandbox first
- Never read a zip while its producer is still writing it
When it happens
Trigger: Calling the ZIP reader (readZip/zip listing via readZipImpl -> parseCentralDirectory) on a file whose central-directory record at the current offset declares nameLength/extraLength/commentLength whose sum pushes `end` beyond `directory.byteLength`, or a crafted/corrupt directory where offset arithmetic overflows.
Common situations: Downloaded or transferred .zip files that were truncated mid-write; archives produced by faulty tools writing wrong lengths; files corrupted by resumable-download resume errors; fuzzed or maliciously crafted archives.
Related errors
- Invalid ARJ ${field}: missing terminator
- Invalid ARJ basic header size
- Invalid LZH level-2 extended header chain
- Invalid ar archive member size
- Invalid ar archive member header
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/562ddf12fed3f9b8.
Report an issue: GitHub.