{"record":{"id":"32e38378896d6116","repo":"can1357/oh-my-pi","slug":"invalid-archive-range","errorCode":null,"errorMessage":"Invalid archive range","messagePattern":"Invalid archive range","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/source.ts","lineNumber":17,"sourceCode":"import { LRUCache } from \"../lru\";\nimport { ArchiveError } from \"./error\";\n\n/**\n * A byte window into an archive — file-backed (lazy, ranged reads) or\n * in-memory. Format readers index through this so ZIP/ASAR/RAR payloads are\n * only read when a member is actually extracted.\n */\nexport interface ByteSource {\n\treadonly size: number;\n\tread(start: number, end: number): Promise<Uint8Array>;\n}\n\n/** Reject a nonsensical `[start, end)` range before any read. */\nexport function assertValidRange(start: number, end: number): void {\n\tif (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start) {\n\t\tthrow new ArchiveError(\"Invalid archive range\");\n\t}\n}\n\n/** Read an exact in-memory range, throwing (not clamping) when it runs past the buffer. */\nexport function readMemoryRange(buffer: Uint8Array, start: number, end: number): Uint8Array {\n\tassertValidRange(start, end);\n\tif (end > buffer.byteLength) {\n\t\tthrow new ArchiveError(\"Invalid archive: truncated data\");\n\t}\n\treturn buffer.subarray(start, end);\n}\n\n/** Wrap borrowed bytes as a {@link ByteSource}. */\nexport function memoryByteSource(buffer: Uint8Array): ByteSource {\n\treturn {\n\t\tsize: buffer.byteLength,\n\t\tasync read(start, end) {\n\t\t\treturn readMemoryRange(buffer, start, end);","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/source.ts#L1-L35","documentation":"`assertValidRange` validates every `[start, end)` byte range before any read in the archive ByteSource layer. It rejects ranges where start or end is not a safe integer, start is negative, or end is less than start — i.e. ranges that could never name a valid byte span. This is a defensive pre-condition shared by memory and file sources so downstream code never sees nonsensical offsets.","triggerScenarios":"Calling `readMemoryRange`/`read` (or any archive API that reads ranges) with a negative offset, a fractional or NaN offset, an offset exceeding Number.MAX_SAFE_INTEGER, or an end before start — typically from bad arithmetic on header-derived sizes/offsets.","commonSituations":"Reading offsets from a corrupt archive header where a size field is huge (overflowing past MAX_SAFE_INTEGER or wrapping negative); buggy caller math passing (start, length) instead of (start, end).","solutions":["Audit the caller's range math — confirm you pass (start, end) as an exclusive end, not (start, length)","Validate offsets/sizes parsed from archive headers against source.size before use","Clamp computed ranges to [0, size] with Math.max/Math.min and re-check before reading"],"exampleFix":"// before: passing length as end\nawait source.read(offset, entrySize);\n// after: compute the exclusive end and validate\nconst end = offset + entrySize;\nif (!Number.isSafeInteger(end) || end > source.size) throw new Error(\"bad entry range\");\nawait source.read(offset, end);","handlingStrategy":"validation","validationCode":"function safeRange(start: number, end: number, size?: number): void {\n\tif (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start) throw new Error(`invalid range [${start}, ${end})`);\n\tif (size !== undefined && end > size) throw new Error(`range end ${end} exceeds size ${size}`);\n}","typeGuard":"function isValidRange(start: unknown, end: unknown): boolean {\n\treturn typeof start === \"number\" && typeof end === \"number\" && Number.isSafeInteger(start) && Number.isSafeInteger(end) && start >= 0 && end >= start;\n}","tryCatchPattern":"try {\n\tconst bytes = await src.read(start, end);\n} catch (err) {\n\tif (err instanceof ArchiveError && err.message === \"Invalid archive range\") {\n\t\tlogger.error(\"bad read range\", { start, end });\n\t}\n\tthrow err;\n}","preventionTips":["Pass an exclusive end, not a length: read(start, start + length)","Clamp computed ranges to [0, size] with Math.max/Math.min","Sanitize offsets parsed from untrusted archive metadata before use"],"tags":["validation","range","argument-error"],"backgroundTag":"invalid-byte-range","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}