{"record":{"id":"ba93dfa1563a1d25","repo":"can1357/oh-my-pi","slug":"unsupported-multi-volume-cab-archive-previous-nex","errorCode":null,"errorMessage":"Unsupported multi-volume CAB archive (previous/next cabinet link)","messagePattern":"Unsupported multi-volume CAB archive \\(previous/next cabinet link\\)","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/cab.ts","lineNumber":264,"sourceCode":"\tif (!hasSignature(fixed)) throw new ArchiveError(`Invalid CAB archive: expected ${CAB_SIGNATURE} signature`);\n\tif (readUInt32LE(fixed, 4) !== 0 || readUInt32LE(fixed, 12) !== 0 || readUInt32LE(fixed, 20) !== 0) {\n\t\tthrow new ArchiveError(\"Invalid CAB archive: reserved CFHEADER fields must be zero\");\n\t}\n\tconst cabinetSize = readUInt32LE(fixed, 8);\n\tif (cabinetSize < FIXED_HEADER_SIZE || cabinetSize > source.size) {\n\t\tthrow new ArchiveError(\"Invalid CAB archive: declared cabinet size is out of bounds\");\n\t}\n\tconst fileTableOffset = readUInt32LE(fixed, 16);\n\tif (fileTableOffset < FIXED_HEADER_SIZE || fileTableOffset > cabinetSize) {\n\t\tthrow new ArchiveError(\"Invalid CAB archive: CFFILE table offset is out of bounds\");\n\t}\n\tif (fixed[24] !== 3 || fixed[25] !== 1) {\n\t\tthrow new ArchiveError(`Unsupported CAB format version ${fixed[25]}.${fixed[24]} (expected 1.3)`);\n\t}\n\tconst folderCount = readUInt16LE(fixed, 26);\n\tconst fileCount = readUInt16LE(fixed, 28);\n\tconst flags = readUInt16LE(fixed, 30);\n\tif (flags & 0x0003) throw new ArchiveError(\"Unsupported multi-volume CAB archive (previous/next cabinet link)\");\n\tassertEntryCount(folderCount + fileCount, options.limits);\n\tif (folderCount === 0 && fileCount !== 0)\n\t\tthrow new ArchiveError(\"Invalid CAB archive: files exist without a folder\");\n\n\tlet headerReserveSize = 0;\n\tlet folderReserveSize = 0;\n\tlet dataReserveSize = 0;\n\tlet folderTableOffset = FIXED_HEADER_SIZE;\n\tif (flags & 0x0004) {\n\t\tconst reserveHeader = await readExact(source, FIXED_HEADER_SIZE, FIXED_HEADER_SIZE + 4, cabinetSize);\n\t\theaderReserveSize = readUInt16LE(reserveHeader, 0);\n\t\tfolderReserveSize = reserveHeader[2]!;\n\t\tdataReserveSize = reserveHeader[3]!;\n\t\tif (headerReserveSize > 60_000)\n\t\t\tthrow new ArchiveError(\"Invalid CAB archive: CFHEADER reserve area exceeds 60000 bytes\");\n\t\tfolderTableOffset += 4 + headerReserveSize;\n\t}\n\tconst folderRecordSize = 8 + folderReserveSize;","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/cab.ts#L246-L282","documentation":"CAB's CFHEADER flags bit 0 (prev cabinet) and bit 1 (next cabinet) indicate a multi-volume cabinet set linked via szCabinetPrev/szCabinetNext. The library handles only single-volume cabinets and throws ArchiveError when either bit is set.","triggerScenarios":"Calling readCab() on a CAB where the flags word (offset 30) has 0x0001 or 0x0002 set — i.e. part of a split .cab set such as disk-spanned installers (data1.cab/data2.cab style sets produced by MakeCAB /i or DIAMOND).","commonSituations":"Multi-disk software distributions from the 90s/2000s, large InstallShield/MSI package sets split across several .cab files, and archives where a continuation cabinet references its neighbors.","solutions":["Locate the first volume of the set and merge/concatenate the volumes with a dedicated splitter tool before reading, if the producer supports it.","Extract each volume's contents separately with cabextract and process them as independent archives.","Repackage the data into a single self-contained cabinet (e.g. with makecab without spanning) if you control the source.","If you only need entries from one volume, verify that volume's flags bit is clear before reading."],"exampleFix":"// before\nconst entries = await readCab(await Bun.file(\"disk2.cab\").arrayBuffer()); // flags set: part of spanned set\n// after\nimport { $ } from \"bun\";\nawait $`cabextract -d out disk1.cab disk2.cab`.quiet(); // extract volumes with a spanning-aware tool","handlingStrategy":"validation","validationCode":"const buf = new Uint8Array(await Bun.file(path).arrayBuffer());\nconst flags = buf[30] | (buf[31]! << 8);\nif (flags & 0x0003) throw new Error(\"Multi-volume CAB set detected; extract volumes with cabextract first\");","typeGuard":null,"tryCatchPattern":"try {\n\tconst entries = await readCab(source);\n} catch (err) {\n\tif (err instanceof ArchiveError && err.message.includes(\"multi-volume CAB\")) {\n\t\t// delegate to cabextract or process each volume separately\n\t\treturn null;\n\t}\n\tthrow err;\n}","preventionTips":["Detect the multi-volume flags (0x0003) up front and route those files to a spanning-aware tool.","When packaging, avoid cabinet spanning; keep data in a single self-contained volume.","Document that this reader handles single-volume cabinets only."],"tags":["archive","cab","unsupported-feature","multi-volume"],"backgroundTag":"unsupported-archive-feature","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}