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
- Verify with `unzip -t` or `zipinfo -v`; if standard tools also fail, the archive is malformed.
- Recreate the archive with a mature writer (Info-ZIP, 7-Zip, Python zipfile) and force ZIP64 where needed.
- Repair with `zip -FF broken.zip --out fixed.zip` and retry.
- 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
- Create >4GB or >65535-entry archives only with mature writers (Info-ZIP, 7-Zip, Python zipfile).
- Validate large archives with `unzip -t` right after production/transfer.
- If writing zips yourself, always emit the ZIP64 locator + EOCD whenever you use sentinel values.
- Keep checksums for large archives to distinguish corruption from format bugs.
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
- Invalid ZIP archive: central directory is out of bounds or m
- Invalid ZIP archive: truncated central directory
- Invalid ZIP archive: malformed NTFS extra field
- Invalid ZIP archive: truncated extra-field header
- Invalid ZIP archive: malformed extra field
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/825fd2a276a8874d.
Report an issue: GitHub.