{"record":{"id":"a210b8266c63beda","repo":"can1357/oh-my-pi","slug":"invalid-zip-archive-what-exceeds-archive-size","errorCode":null,"errorMessage":"Invalid ZIP archive: ${what} exceeds archive size","messagePattern":"Invalid ZIP archive: (.+?) exceeds archive size","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/zip.ts","lineNumber":107,"sourceCode":"}\n\ninterface ParsedZipEntry {\n\tentry: ArchiveIndexEntry;\n\tisSymlink: boolean;\n}\n\nfunction archiveError(error: unknown, context: string): ArchiveError {\n\tif (error instanceof ArchiveError) return error;\n\treturn new ArchiveError(`${context}: ${error instanceof Error ? error.message : String(error)}`);\n}\n\nfunction checkedEnd(start: number, size: number, archiveSize: number, what: string): number {\n\tif (!Number.isSafeInteger(start) || !Number.isSafeInteger(size) || start < 0 || size < 0) {\n\t\tthrow new ArchiveError(`Invalid ZIP archive: ${what} has an invalid range`);\n\t}\n\tconst end = start + size;\n\tif (!Number.isSafeInteger(end) || end > archiveSize) {\n\t\tthrow new ArchiveError(`Invalid ZIP archive: ${what} exceeds archive size`);\n\t}\n\treturn end;\n}\n\nfunction findEocd(tail: Uint8Array): number {\n\tfor (let offset = tail.byteLength - EOCD_LENGTH; offset >= 0; offset--) {\n\t\tif (readUInt32LE(tail, offset) !== EOCD_SIGNATURE) continue;\n\t\tif (offset + EOCD_LENGTH + readUInt16LE(tail, offset + 20) === tail.byteLength) return offset;\n\t}\n\tthrow new ArchiveError(\"Invalid ZIP archive: missing end of central directory\");\n}\n\nasync function readZip64Info(\n\tsource: ByteSource,\n\ttail: Uint8Array,\n\ttailStart: number,\n\teocdOffset: number,\n): Promise<CentralDirectoryInfo | undefined> {","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/zip.ts#L89-L125","documentation":"checkedEnd in zip.ts throws this when start + size computes past the end of the archive (or overflows a safe integer). It protects callers from records that claim data beyond the file, which would otherwise produce out-of-bounds reads or undefined behavior downstream.","triggerScenarios":"readZip parsing a central directory entry, local header, or data span whose declared offset+size exceeds source size; typical for truncated files or forged header values.","commonSituations":"Partially downloaded ZIPs; archives on a disk that filled up mid-write; entries listed in the central directory whose local data was never written.","solutions":["Run `unzip -t` / re-download the archive; this error almost always means real truncation","If the ZIP came from your own writer, ensure the central directory is written after all entry data","For large archives, confirm the file wasn't cut by a size limit (upload cap, tar slice, git-lfs filter)"],"exampleFix":"// before\nconst zip = await readZip(await Bun.file(upload.path).bytes());\n// after\nconst file = Bun.file(upload.path);\nif (file.size < upload.expectedBytes) throw new Error(`zip truncated: ${file.size} < ${upload.expectedBytes}`);\nconst zip = await readZip(await file.bytes());","handlingStrategy":"validation","validationCode":"const file = Bun.file(zipPath);\nif (file.size < minimumExpectedBytes) {\n  throw new Error(`zip truncated: ${file.size} bytes, expected >= ${minimumExpectedBytes}`);\n}\nconst bytes = await file.bytes();\nconst dv = new DataView(bytes.buffer);\nif (dv.getUint32(bytes.length - 22, true) !== 0x06054b50 && !findEocdSig(bytes)) {\n  throw new Error(\"zip EOCD not at expected location — likely truncated\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  const zip = await readZip(bytes);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"exceeds archive size\")) {\n    return { ok: false, reason: \"truncated zip\" };\n  }\n  throw err;\n}","preventionTips":["Verify total file size against a manifest/checksum after download","Watch for size caps (upload limits, git filters, tar slices) that silently truncate","Test archives with `unzip -t` before feeding them to automated pipelines"],"tags":["zip","archive","truncated","bounds-check"],"backgroundTag":"zip-central-directory-corrupt","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}