{"record":{"id":"5dbc3d60dc451728","repo":"can1357/oh-my-pi","slug":"invalid-cab-archive-expected-cab-signature-sig","errorCode":null,"errorMessage":"Invalid CAB archive: expected ${CAB_SIGNATURE} signature","messagePattern":"Invalid CAB archive: expected (.+?) signature","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/cab.ts","lineNumber":246,"sourceCode":"\t}\n\n\tasync read(size: number, memberPath: string): Promise<Uint8Array> {\n\t\tif (size !== this.#declaredSize) {\n\t\t\tthrow new ArchiveError(`Invalid CAB archive: size changed while extracting '${memberPath}'`);\n\t\t}\n\t\tconst folder = await this.#folder.readAll();\n\t\tconst end = this.#offset + size;\n\t\tif (!Number.isSafeInteger(end) || this.#offset < 0 || end > folder.byteLength) {\n\t\t\tthrow new ArchiveError(`Invalid CAB archive: member '${memberPath}' is outside its folder data`);\n\t\t}\n\t\treturn folder.slice(this.#offset, end);\n\t}\n}\n\nasync function readCabArchive(source: ByteSource, options: Parameters<FormatReader>[1]): Promise<ArchiveIndexEntry[]> {\n\tif (source.size < FIXED_HEADER_SIZE) throw new ArchiveError(\"Invalid CAB archive: truncated CFHEADER\");\n\tconst fixed = await readExact(source, 0, FIXED_HEADER_SIZE);\n\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)\");","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/cab.ts#L228-L264","documentation":"readCabArchive reads the 36-byte fixed CFHEADER and checks its first four bytes for the 'MSCF' cabinet signature. If those bytes differ, the file is not a Microsoft CAB archive, so parsing is aborted with an ArchiveError before any further structure is trusted. This is the library's first-line sanity check against feeding it a completely wrong file format.","triggerScenarios":"Calling readCab() (which delegates to readCabArchive) on a file whose first 4 bytes are not 4D 53 43 46 ('MSCF') — e.g. a ZIP, MSI container, or any non-CAB binary — while the file is at least FIXED_HEADER_SIZE (36) bytes long so the truncation check passes first.","commonSituations":"Pointing the reader at the wrong file in an installer directory, extracting CAB payloads embedded in MSI/EXE wrappers without locating the inner cabinet offset, downloading a corrupted or HTML-error-page file, or confusing MSZIP-style archives with actual CAB format.","solutions":["Verify the file path points to an actual .cab file; check the first 4 bytes are 'MSCF' before calling readCab.","If the cabinet is embedded inside an MSI/EXE, locate the inner cabinet offset and pass a source sliced at that offset rather than the wrapper itself.","Re-download or re-export the file; a truncated-to-36+ bytes non-CAB blob usually means transfer corruption or the wrong artifact.","Check whether the file is a related Microsoft format (e.g. MSI, MSP) and use the appropriate reader instead."],"exampleFix":"// before\nconst entries = await readCab(Bun.file(\"setup.msi\"));\n// after\nconst buf = new Uint8Array(await Bun.file(\"data1.cab\").arrayBuffer());\nif (new TextDecoder().decode(buf.slice(0, 4)) !== \"MSCF\") throw new Error(\"not a CAB file\");\nconst entries = await readCab(buf);","handlingStrategy":"validation","validationCode":"const head = new Uint8Array(buf.slice(0, 4));\nconst isCab = head[0] === 0x4d && head[1] === 0x53 && head[2] === 0x43 && head[3] === 0x46;\nif (!isCab) throw new Error(`Not a CAB archive (got magic ${Buffer.from(head).toString(\"hex\")})`);","typeGuard":"function looksLikeCab(bytes: Uint8Array): boolean {\n\treturn bytes.length >= 4 && bytes[0] === 0x4d && bytes[1] === 0x53 && bytes[2] === 0x43 && bytes[3] === 0x46;\n}","tryCatchPattern":"try {\n\tconst entries = await readCab(source);\n} catch (err) {\n\tif (err instanceof ArchiveError && err.message.includes(\"expected MSCF signature\")) {\n\t\t// wrong file type: surface a clear \"not a CAB\" message to the user\n\t\treturn null;\n\t}\n\tthrow err;\n}","preventionTips":["Check the 'MSCF' magic before handing any file to readCab.","For embedded cabinets, slice the source at the verified inner-cabinet offset first.","Verify download integrity (size/checksum) before parsing."],"tags":["archive","cab","invalid-signature","file-format"],"backgroundTag":"invalid-archive-signature","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}