{"record":{"id":"108caf73c7d7f5ff","repo":"can1357/oh-my-pi","slug":"invalid-cpio-archive-field-is-not-a-valid-base","errorCode":null,"errorMessage":"Invalid CPIO archive: ${field} is not a valid base-${radix} number","messagePattern":"Invalid CPIO archive: (.+?) is not a valid base-(.+?) number","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/cpio.ts","lineNumber":113,"sourceCode":"\t\t!Number.isSafeInteger(end) ||\n\t\tstart < 0 ||\n\t\tend < start ||\n\t\tend > bytes.byteLength\n\t) {\n\t\tthrow new ArchiveError(`Invalid CPIO archive: truncated ${what}`);\n\t}\n}\n\nfunction parseDigits(bytes: Uint8Array, offset: number, length: number, radix: 8 | 16, field: string): number {\n\trequireRange(bytes, offset, offset + length, `${field} field`);\n\tlet value = 0;\n\tfor (let index = offset; index < offset + length; index++) {\n\t\tconst code = bytes[index]!;\n\t\tlet digit: number;\n\t\tif (code >= 0x30 && code <= 0x39) digit = code - 0x30;\n\t\telse if (radix === 16 && code >= 0x41 && code <= 0x46) digit = code - 0x41 + 10;\n\t\telse if (radix === 16 && code >= 0x61 && code <= 0x66) digit = code - 0x61 + 10;\n\t\telse throw new ArchiveError(`Invalid CPIO archive: ${field} is not a valid base-${radix} number`);\n\t\tif (digit >= radix) throw new ArchiveError(`Invalid CPIO archive: ${field} is not a valid base-${radix} number`);\n\t\tvalue = value * radix + digit;\n\t}\n\tif (!Number.isSafeInteger(value)) throw new ArchiveError(`Invalid CPIO archive: ${field} is too large`);\n\treturn value;\n}\n\nfunction parseHeader(bytes: Uint8Array, offset: number): ParsedHeader {\n\trequireRange(bytes, offset, offset + 2, \"header\");\n\tconst first = bytes[offset]!;\n\tconst second = bytes[offset + 1]!;\n\tif ((first === 0xc7 && second === 0x71) || (first === 0x71 && second === 0xc7)) {\n\t\trequireRange(bytes, offset, offset + BINARY_HEADER_SIZE, \"old binary header\");\n\t\tconst littleEndian = first === 0xc7;\n\t\tconst read16 = littleEndian ? readUInt16LE : readUInt16BE;\n\t\tconst read32Words = (fieldOffset: number): number =>\n\t\t\tread16(bytes, offset + fieldOffset) * 0x10000 + read16(bytes, offset + fieldOffset + 2);\n\t\treturn {","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/cpio.ts#L95-L131","documentation":"parseDigits reads a fixed-width ASCII numeric header field of a CPIO entry and accumulates it digit by digit. The library throws this ArchiveError when a byte in the field is neither an ASCII digit valid for the field's radix (base-8 for odc/binary-style fields, base-16 hex for newc/new ASCII fields), meaning the archive header is corrupt or the format was misidentified. This is a strict structural validation: CPIO headers store all metadata fields as fixed-width ASCII numbers, so any stray byte breaks parsing.","triggerScenarios":"Calling readCpio/readRpmArchive/entries on a buffer whose header contains a non-digit byte in an ASCII metadata field, e.g. a newc header where an 8-char hex field contains 'G'-'Z' or punctuation, or an odc 6/11-char octal field containing '8', '9', or a NUL/space byte. Also triggered when the parser aligns on a wrong offset after mis-detecting a magic, so it reads garbage as field bytes.","commonSituations":"Corrupted or truncated downloads of .cpio/.rpm payloads; archives produced by nonstandard tools that pad fields with spaces or NULs instead of leading zeros; attempting to parse a different archive format (tar, ar) that happens to be fed to the CPIO reader; hand-crafted or fuzzed archive bytes.","solutions":["Regenerate the archive with a standard tool (cpio -H newc, or rpm2cpio for RPM payload) and re-download/verify checksums","Verify you are feeding the right format — use sniffCpio on the buffer head before parsing","Inspect the header bytes at the failing offset and confirm field encoding matches the format (hex for newc, octal for odc)","If parsing RPM payloads, extract the cpio payload exactly (rpm2cpio) — do not pass raw RPM header bytes"],"exampleFix":"// before: parsing arbitrary bytes directly\nconst entries = await readCpio(source, options);\n// after: sniff first and produce the archive with a standard format\nconst head = new Uint8Array(await source.slice(0, 6));\nif (!sniffCpio(head)) throw new Error('not a supported CPIO archive');\nconst entries = await readCpio(source, options);","handlingStrategy":"validation","validationCode":"import { sniffCpio } from '<lib>/ar/cpio';\nconst head = new Uint8Array(await source.slice(0, 110));\nif (!sniffCpio(head)) throw new Error('input is not a supported CPIO stream');","typeGuard":"function isAsciiDigits(bytes: Uint8Array, radix: 8 | 16): boolean {\n  return [...bytes].every(c =>\n    (c >= 0x30 && c <= 0x37) ||\n    (radix === 16 && ((c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66) || (c >= 0x38 && c <= 0x39))));\n}","tryCatchPattern":"try {\n  const entries = await readCpio(source, options);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message.includes('not a valid base-')) {\n    // treat as corrupt/non-CPIO input: fall back to another format reader or reject\n  } else throw err;\n}","preventionTips":["Sniff the format (sniffCpio / magic bytes) before parsing","Only feed the CPIO reader decompressed cpio payloads (rpm2cpio output), not raw RPM or tar bytes","Verify file checksums after download to catch transfer corruption","Prefer archives produced by GNU cpio or bsdtar over custom writers"],"tags":["archive","cpio","corrupt-archive","parsing"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}