{"record":{"id":"fef991c59773d11b","repo":"can1357/oh-my-pi","slug":"unable-to-read-cab-archive-error-instanceof-err","errorCode":null,"errorMessage":"Unable to read CAB archive: ${error instanceof Error ? error.message : String(error)}","messagePattern":"Unable to read CAB archive: (.+?)","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/cab.ts","lineNumber":50,"sourceCode":"\tparameter: number;\n\trequiredSize: number;\n}\n\nasync function readExact(\n\tsource: ByteSource,\n\tstart: number,\n\tend: number,\n\tcabinetSize = source.size,\n): Promise<Uint8Array> {\n\tif (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end > cabinetSize) {\n\t\tthrow new ArchiveError(\"Invalid CAB archive: metadata range is out of bounds\");\n\t}\n\tlet bytes: Uint8Array;\n\ttry {\n\t\tbytes = await source.read(start, end);\n\t} catch (error) {\n\t\tif (error instanceof ArchiveError) throw error;\n\t\tthrow new ArchiveError(`Unable to read CAB archive: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tif (bytes.byteLength !== end - start) throw new ArchiveError(\"Invalid CAB archive: truncated data\");\n\treturn bytes;\n}\n\nfunction hasSignature(bytes: Uint8Array): boolean {\n\treturn bytes.byteLength >= 4 && bytes[0] === 0x4d && bytes[1] === 0x53 && bytes[2] === 0x43 && bytes[3] === 0x46;\n}\n\nfunction cabChecksum(bytes: Uint8Array, initial = 0): number {\n\tlet checksum = initial >>> 0;\n\tlet offset = 0;\n\twhile (offset + 4 <= bytes.byteLength) {\n\t\tchecksum ^= readUInt32LE(bytes, offset);\n\t\toffset += 4;\n\t}\n\tconst remaining = bytes.byteLength - offset;\n\tlet remainder = 0;","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/cab.ts#L32-L68","documentation":"This ArchiveError wraps any unexpected failure from the underlying byte-source's read() while readExact fetches a region of a CAB archive. The library deliberately converts non-ArchiveError read failures (I/O errors, permission denials, closed streams) into a uniform 'Unable to read CAB archive: <cause>' message so callers only need to handle ArchiveError. The original error's message is appended for diagnosis.","triggerScenarios":"Calling header(), bytes(), fixed(), reserveHeader(), or fileTable() when source.read(start, end) rejects with something other than ArchiveError — e.g. the backing file descriptor was closed, the file was deleted mid-read, or a network-backed source dropped its connection.","commonSituations":"Reading a .cab from an NFS/cloud mount that goes away; passing a Blob/File handle whose underlying file was moved; a custom ArchiveSource whose read() throws a non-ArchiveError like ENOSPC or EACCES; reading a file being concurrently written/truncated by another process.","solutions":["Read the appended cause (text after 'Unable to read CAB archive: ') and fix the underlying I/O problem (permissions, disk space, missing file).","Verify the source path exists and is readable before constructing the CAB reader: fs.access(path, fs.constants.R_OK).","Keep the file handle/stream open for the lifetime of the reader — do not close the descriptor before calling header()/fileTable().","If using a custom ArchiveSource, wrap known failures in ArchiveError yourself or ensure read() only throws for genuine read failures.","Retry once if the source is transient (network mount flake) after confirming it is accessible."],"exampleFix":"// before: reading from a stream closed earlier\nconst reader = await openCab(path);\nstream.close();\nawait reader.fileTable(); // throws Unable to read CAB archive: EBADF\n// after: read while handle is open\nconst reader = await openCab(path);\ntry {\n  await reader.fileTable();\n} finally {\n  await stream.close();\n}","handlingStrategy":"try-catch","validationCode":"import { accessSync, constants } from 'node:fs';\ntry { accessSync(path, constants.R_OK); } catch { throw new Error(`CAB source unreadable: ${path}`); }","typeGuard":"function isArchiveError(e: unknown): e is ArchiveError {\n  return e instanceof ArchiveError;\n}","tryCatchPattern":"try {\n  const table = await reader.fileTable();\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.startsWith('Unable to read CAB archive:')) {\n    const cause = err.message.slice('Unable to read CAB archive: '.length);\n    console.error(`CAB source read failed: ${cause}`);\n  } else throw err;\n}","preventionTips":["Keep the source handle open until all CAB reads complete.","Check file readability (access) before opening the archive.","Prefer plain file paths over volatile network mounts for archive sources.","For custom ArchiveSource implementations, wrap expected failures in ArchiveError."],"tags":["io","archive","cab","read-failure"],"backgroundTag":"archive-read-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}