{"record":{"id":"b6f2b8dd2312dc56","repo":"can1357/oh-my-pi","slug":"invalid-zip-archive-missing-zip64-end-of-central","errorCode":null,"errorMessage":"Invalid ZIP archive: missing ZIP64 end of central directory","messagePattern":"Invalid ZIP archive: missing ZIP64 end of central directory","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/zip.ts","lineNumber":163,"sourceCode":"\t\tconst record = await source.read(candidate, candidate + ZIP64_EOCD_LENGTH);\n\t\tif (record.byteLength !== ZIP64_EOCD_LENGTH || readUInt32LE(record, 0) !== ZIP64_EOCD_SIGNATURE) continue;\n\t\tconst extensibleSize = readUInt64LE(record, 4);\n\t\tif (extensibleSize < 44 || candidate + 12 + extensibleSize !== locatorOffset) continue;\n\t\tif (readUInt32LE(record, 16) !== 0 || readUInt32LE(record, 20) !== 0) {\n\t\t\tthrow new ArchiveError(\"Multi-volume ZIP archives are not supported\");\n\t\t}\n\t\tconst entriesOnDisk = readUInt64LE(record, 24);\n\t\tconst entries = readUInt64LE(record, 32);\n\t\tif (entriesOnDisk !== entries) throw new ArchiveError(\"Multi-volume ZIP archives are not supported\");\n\t\treturn {\n\t\t\tentries,\n\t\t\tsize: readUInt64LE(record, 40),\n\t\t\toffset: readUInt64LE(record, 48),\n\t\t\tphysicalEnd: candidate,\n\t\t\tarchiveOffset: 0,\n\t\t};\n\t}\n\tthrow new ArchiveError(\"Invalid ZIP archive: missing ZIP64 end of central directory\");\n}\n\nasync function locateCentralDirectory(source: ByteSource, info: CentralDirectoryInfo): Promise<number> {\n\tif (info.entries === 0) return info.offset;\n\tconst candidates = [info.offset];\n\tconst adjacent = info.physicalEnd - info.size;\n\tif (adjacent !== info.offset) candidates.push(adjacent);\n\tfor (const offset of candidates) {\n\t\tif (offset < 0 || offset + 4 > source.size || offset + info.size > source.size) continue;\n\t\tconst signature = await source.read(offset, offset + 4);\n\t\tif (signature.byteLength === 4 && readUInt32LE(signature, 0) === CENTRAL_HEADER_SIGNATURE) return offset;\n\t}\n\tthrow new ArchiveError(\"Invalid ZIP archive: central directory is out of bounds or malformed\");\n}\n\nasync function readCentralDirectoryInfo(source: ByteSource, limits: ArchiveLimits): Promise<CentralDirectoryInfo> {\n\tif (source.size < EOCD_LENGTH) throw new ArchiveError(\"Invalid ZIP archive: missing end of central directory\");\n\tconst tailLength = Math.min(source.size, EOCD_LENGTH + MAX_COMMENT_LENGTH);","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/zip.ts#L145-L181","documentation":"readZip64Info is only called when the ZIP needs ZIP64 decoding (the locator was found), yet no candidate location yields a record with the ZIP64 EOCD signature and a consistent extensible-data size ending exactly at the locator. The reader throws this ArchiveError because ZIP64 metadata it was told exists cannot actually be located.","triggerScenarios":"readZip on an archive with a valid ZIP64 locator but a missing, moved, or corrupted ZIP64 EOCD record; prepended data (self-extractor stubs, concatenated files) shifting offsets so the declared ZIP64 EOCD offset no longer matches.","commonSituations":"Very large archives (>4GB or >65535 entries) that require ZIP64, whose final records were truncated; SFX stubs prepended after creation; tools that rewrite the EOCD but not the ZIP64 structures.","solutions":["Re-create the archive with a current zip implementation (Info-ZIP, 7-Zip) that writes ZIP64 correctly","Check for truncation — ZIP64 records are near the end, so a cut file loses them first","If the file has a prepended stub, use a reader path that accounts for the prefix offset, or strip the stub","Validate with `unzip -t`; mismatch there confirms structural damage"],"exampleFix":"// before\nconst zip = await readZip(sfxBytes); // ZIP64 EOCD offset shifted by stub\n// after\nconst raw = await Bun.file(\"app.sfx\").bytes();\nconst stripped = stripSfxStub(raw); // remove prepended bytes so offsets line up\nconst zip = await readZip(stripped);","handlingStrategy":"validation","validationCode":"// require ZIP64 structures before parsing huge zips\nfunction needsZip64AndHasLocator(bytes: Uint8Array): boolean {\n  const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n  // find EOCD\n  for (let i = bytes.length - 22; i >= Math.max(0, bytes.length - 22 - 65535); i--) {\n    if (dv.getUint32(i, true) !== 0x06054b50) continue;\n    const entries = dv.getUint16(i + 10, true);\n    const cdSize = dv.getUint32(i + 12, true);\n    const needs64 = entries === 0xffff || cdSize === 0xffffffff;\n    if (!needs64) return false;\n    // locator must exist just before EOCD\n    const loc = i - 20;\n    return loc >= 0 && dv.getUint32(loc, true) === 0x07064b50;\n  }\n  return false;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const zip = await readZip(bytes);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"missing ZIP64 end of central directory\")) {\n    return rejectUpload(\"zip64 metadata missing/corrupt — archive may be truncated or stub-prefixed\");\n  }\n  throw err;\n}","preventionTips":["For >4GB or >65535-entry archives, verify completeness before parsing — ZIP64 records sit at the tail and are the first casualty of truncation","Strip or account for SFX/prefix stubs that shift ZIP64 offsets","Re-create archives with modern zip tools if third-party software produced malformed ZIP64 structures"],"tags":["zip","zip64","eocd","corrupt-archive"],"backgroundTag":"zip-eocd-missing","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}