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

  1. Re-download or re-export the archive; verify its integrity (checksum) against the source
  2. Test the archive with an independent tool (unzip -t) to confirm it is corrupt rather than a library bug
  3. Ensure the whole file was uploaded/downloaded (compare byte sizes) before reading
  4. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/562ddf12fed3f9b8. Report an issue: GitHub.