can1357/oh-my-pi · error · ArchiveError

Invalid ZIP archive: missing ZIP64 central-directory metadat

Error message

Invalid ZIP archive: missing ZIP64 central-directory metadata

What it means

Thrown by readCentralDirectoryInfo when the EOCD uses ZIP64 sentinel values (0xFFFF entry counts or 0xFFFFFFFF size/offset) — meaning real values live in ZIP64 locator/EOCD records — but no valid ZIP64 EOCD could be found or validated by readZip64Info. The archive claims ZIP64 metadata that does not exist.

Source

Thrown at packages/utils/src/ar/zip.ts:207

	const centralDisk = readUInt16LE(tail, eocdIndex + 6);
	const entriesOnDisk = readUInt16LE(tail, eocdIndex + 8);
	let entries = readUInt16LE(tail, eocdIndex + 10);
	let size = readUInt32LE(tail, eocdIndex + 12);
	let offset = readUInt32LE(tail, eocdIndex + 16);
	if (
		disk !== 0 ||
		centralDisk !== 0 ||
		(entriesOnDisk !== U16_MAX && entries !== U16_MAX && entriesOnDisk !== entries)
	) {
		throw new ArchiveError("Multi-volume ZIP archives are not supported");
	}
	const needsZip64 = entriesOnDisk === U16_MAX || entries === U16_MAX || size === U32_MAX || offset === U32_MAX;
	const zip64 = await readZip64Info(source, tail, tailStart, eocdOffset);
	let physicalEnd = eocdOffset;
	if (zip64) {
		({ entries, size, offset, physicalEnd } = zip64);
	} else if (needsZip64) {
		throw new ArchiveError("Invalid ZIP archive: missing ZIP64 central-directory metadata");
	}
	assertEntryCount(entries, limits);
	assertIndexSize(size, limits, "ZIP central directory");
	if (entries > Math.floor(size / 46)) throw new ArchiveError("Invalid ZIP archive: truncated central directory");
	const declaredOffset = offset;
	const info = { entries, size, offset, physicalEnd, archiveOffset: 0 };
	info.offset = await locateCentralDirectory(source, info);
	info.archiveOffset = info.offset - declaredOffset;
	checkedEnd(info.offset, info.size, source.size, "central directory");
	return info;
}

function filetimeToMs(bytes: Uint8Array, offset: number): number | undefined {
	let value = 0n;
	for (let index = 7; index >= 0; index--) value = (value << 8n) | BigInt(bytes[offset + index]!);
	const milliseconds = value / 10_000n - WINDOWS_EPOCH_FILETIME_MS;
	const number = Number(milliseconds);
	return Number.isSafeInteger(number) ? number : undefined;

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify with `unzip -t` or `zipinfo -v`; if standard tools also fail, the archive is malformed.
  2. Recreate the archive with a mature writer (Info-ZIP, 7-Zip, Python zipfile) and force ZIP64 where needed.
  3. Repair with `zip -FF broken.zip --out fixed.zip` and retry.
  4. If a custom writer emitted sentinel values, fix it to always write the ZIP64 locator + EOCD when using sentinels.

Example fix

// before: reading an archive from a homegrown writer that sets ZIP64 sentinels
const zip = readZip(customWriterOutput);
// after: validate with a reference tool first, or re-zip
await $`7z t ${customWriterOutput}`.nothrow();
await $`zip -r rebuilt.zip ./data`; // standard writer emits proper ZIP64 EOCD
const zip = readZip("rebuilt.zip");
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight with a battle-tested parser before using this library on large archives.
import * as fs from "node:fs/promises";
const size = (await fs.stat(path)).size;
if (size > 4 * 1024 * 1024 * 1024) {
  // must be ZIP64; require the caller to have validated it (e.g. `unzip -t` passed)
  if (!preValidated) throw new Error("oversized zip must be validated as ZIP64 before reading");
}

Try / catch

try {
  const zip = readZip(largeArchivePath);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("missing ZIP64 central-directory metadata")) {
    // archive declares ZIP64 but the records are absent/corrupt: repair or re-zip
    await $`zip -FF ${path} --out ${path}.fixed.zip`.nothrow();
    return readZip(`${path}.fixed.zip`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling zip info/read on an archive whose EOCD fields are the ZIP64 sentinels while the 20-byte ZIP64 locator and 56-byte ZIP64 EOCD are absent, corrupted, or whose declared offsets don't line up (extensible-data size mismatch, wrong totalRecords).

Common situations: Archives larger than 4 GB or with 65535+ entries whose writer set ZIP64 markers but failed to emit the ZIP64 EOCD; third-party writers with incomplete ZIP64 support; corruption of the final bytes of a large archive.

Related errors


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