{"record":{"id":"6735329a7ee3baeb","repo":"can1357/oh-my-pi","slug":"multi-volume-zip-archives-are-not-supported","errorCode":null,"errorMessage":"Multi-volume ZIP archives are not supported","messagePattern":"Multi-volume ZIP archives are not supported","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/zip.ts","lineNumber":136,"sourceCode":"}\n\nasync function readZip64Info(\n\tsource: ByteSource,\n\ttail: Uint8Array,\n\ttailStart: number,\n\teocdOffset: number,\n): Promise<CentralDirectoryInfo | undefined> {\n\tconst locatorOffset = eocdOffset - ZIP64_LOCATOR_LENGTH;\n\tif (locatorOffset < 0) return undefined;\n\tconst locator =\n\t\tlocatorOffset >= tailStart\n\t\t\t? tail.subarray(locatorOffset - tailStart, locatorOffset - tailStart + ZIP64_LOCATOR_LENGTH)\n\t\t\t: await source.read(locatorOffset, eocdOffset);\n\tif (locator.byteLength !== ZIP64_LOCATOR_LENGTH || readUInt32LE(locator, 0) !== ZIP64_LOCATOR_SIGNATURE) {\n\t\treturn undefined;\n\t}\n\tif (readUInt32LE(locator, 4) !== 0 || readUInt32LE(locator, 16) !== 1) {\n\t\tthrow new ArchiveError(\"Multi-volume ZIP archives are not supported\");\n\t}\n\n\tconst declaredOffset = readUInt64LE(locator, 8);\n\tconst candidates = [declaredOffset];\n\tconst adjacentOffset = locatorOffset - ZIP64_EOCD_LENGTH;\n\tif (adjacentOffset >= 0 && adjacentOffset !== declaredOffset) candidates.push(adjacentOffset);\n\tfor (const candidate of candidates) {\n\t\tif (candidate < 0 || candidate + ZIP64_EOCD_LENGTH > source.size) continue;\n\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\");","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/zip.ts#L118-L154","documentation":"When a ZIP64 locator is present, readZip64Info checks the locator's 'total number of disks' field (offset 4) and the 'which disk the ZIP64 EOCD is on' field (offset 16). Both must be 0/1 for a single-volume archive. Any other value means the archive spans multiple disks/volumes, which this reader does not support.","triggerScenarios":"readZip on a ZIP64 archive whose locator declares a non-zero disk count or points the ZIP64 EOCD at a disk other than the last — i.e. a genuinely multi-volume (split) ZIP64 archive.","commonSituations":"ZIP archives split across multiple files/disks by tools like 7-Zip or WinZip spanned-backup mode; ZIP64 files produced by unusual writers that leave disk fields set to non-zero values.","solutions":["Reassemble/re-extract the split archive with the original tool, then feed the single reassembled .zip to the reader","Re-create the archive as a single-volume ZIP (`zip -r out.zip dir`, avoid `-s` split options)","If your writer emits ZIP64, ensure locator disk fields are zeroed"],"exampleFix":"// before\n$`7z a -v100m out.zip bigdir`; // spanned volumes -> readZip throws\n// after\n$`7z a out.zip bigdir`; // single-volume archive\nconst zip = await readZip(await Bun.file(\"out.zip\").bytes());","handlingStrategy":"validation","validationCode":"// Detect a ZIP64 locator early and reject multi-disk archives before parsing\nfunction isSpannedZip64(bytes: Uint8Array): boolean {\n  const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n  for (let i = bytes.length - 20; i >= 0 && i > bytes.length - 22 - 20 - 65535; i--) {\n    if (dv.getUint32(i, true) !== 0x07064b50) continue; // zip64 locator sig\n    return dv.getUint32(i + 4, true) !== 0 || dv.getUint32(i + 16, true) !== 1;\n  }\n  return false;\n}\nif (isSpannedZip64(bytes)) throw new Error(\"multi-volume zip: reassemble before parsing\");","typeGuard":null,"tryCatchPattern":"try {\n  const zip = await readZip(bytes);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes(\"Multi-volume\")) {\n    return instructUserToRejoin(\"this zip is split across volumes; rejoin with the original tool\");\n  }\n  throw err;\n}","preventionTips":["Don't create spanned archives (-s / -v split options); use single-file zips for programmatic parsing","If receiving spanned sets, rejoin them with the original archiving tool before parsing","Document the single-volume limitation where archives enter your system"],"tags":["zip","zip64","multi-volume","unsupported"],"backgroundTag":"zip-multi-volume-unsupported","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}