{"record":{"id":"562ddf12fed3f9b8","repo":"can1357/oh-my-pi","slug":"invalid-zip-archive-truncated-central-directory-e","errorCode":null,"errorMessage":"Invalid ZIP archive: truncated central-directory entry","messagePattern":"Invalid ZIP archive: truncated central-directory entry","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/zip.ts","lineNumber":478,"sourceCode":"\t\tconst flags = readUInt16LE(directory, offset + 8);\n\t\tconst method = readUInt16LE(directory, offset + 10);\n\t\tconst dosTime = readUInt16LE(directory, offset + 12);\n\t\tconst dosDate = readUInt16LE(directory, offset + 14);\n\t\tconst crc = readUInt32LE(directory, offset + 16);\n\t\tconst compressedRaw = readUInt32LE(directory, offset + 20);\n\t\tconst uncompressedRaw = readUInt32LE(directory, offset + 24);\n\t\tconst nameLength = readUInt16LE(directory, offset + 28);\n\t\tconst extraLength = readUInt16LE(directory, offset + 30);\n\t\tconst commentLength = readUInt16LE(directory, offset + 32);\n\t\tconst diskStartRaw = readUInt16LE(directory, offset + 34);\n\t\tconst externalAttributes = readUInt32LE(directory, offset + 38);\n\t\tconst localOffsetRaw = readUInt32LE(directory, offset + 42);\n\t\tconst nameStart = offset + 46;\n\t\tconst extraStart = nameStart + nameLength;\n\t\tconst commentStart = extraStart + extraLength;\n\t\tconst end = commentStart + commentLength;\n\t\tif (!Number.isSafeInteger(end) || end > directory.byteLength) {\n\t\t\tthrow new ArchiveError(\"Invalid ZIP archive: truncated central-directory entry\");\n\t\t}\n\t\tassertArchivePathBytes(nameLength, \"member path\", options.limits.maxPathBytes);\n\t\tconst rawName = directory.subarray(nameStart, extraStart);\n\t\tconst extra = parseExtra(directory.subarray(extraStart, commentStart), rawName);\n\t\tconst values = applyZip64Values(\n\t\t\textra.zip64,\n\t\t\t{\n\t\t\t\tcompressedSize: compressedRaw,\n\t\t\t\tuncompressedSize: uncompressedRaw,\n\t\t\t\tlocalHeaderOffset: localOffsetRaw,\n\t\t\t\tdiskStart: diskStartRaw,\n\t\t\t},\n\t\t\t{\n\t\t\t\tcompressedSize: compressedRaw === U32_MAX,\n\t\t\t\tuncompressedSize: uncompressedRaw === U32_MAX,\n\t\t\t\tlocalHeaderOffset: localOffsetRaw === U32_MAX,\n\t\t\t\tdiskStart: diskStartRaw === U16_MAX,\n\t\t\t},","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/zip.ts#L460-L496","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: reading a possibly partial download\nawait readZip(Bun.file(url));\n// after: check expected size first\nconst file = Bun.file(path);\nif (file.size < expectedSize) throw new Error(`Incomplete download: ${file.size}/${expectedSize}`);\nawait readZip(file);","handlingStrategy":"validation","validationCode":"const stat = await Bun.file(zipPath).stat();\nif (!stat.isFile() || stat.size === 0) throw new Error('not a usable zip file');\nif (expectedSize && stat.size !== expectedSize) throw new Error(`truncated: ${stat.size}/${expectedSize}`);","typeGuard":null,"tryCatchPattern":"try {\n  const entries = await readZip(file);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('truncated central-directory entry')) {\n    throw new Error(`Archive corrupt: ${file.name} — re-download required`, { cause: err });\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["zip","archive","corrupt-file","parsing"],"backgroundTag":"truncated-zip-central-directory","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}