can1357/oh-my-pi · error · ArchiveError

Invalid CPIO archive: ${field} is not a valid base-${radix}

Error message

Invalid CPIO archive: ${field} is not a valid base-${radix} number

What it means

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.

Source

Thrown at packages/utils/src/ar/cpio.ts:113

		!Number.isSafeInteger(end) ||
		start < 0 ||
		end < start ||
		end > bytes.byteLength
	) {
		throw new ArchiveError(`Invalid CPIO archive: truncated ${what}`);
	}
}

function parseDigits(bytes: Uint8Array, offset: number, length: number, radix: 8 | 16, field: string): number {
	requireRange(bytes, offset, offset + length, `${field} field`);
	let value = 0;
	for (let index = offset; index < offset + length; index++) {
		const code = bytes[index]!;
		let digit: number;
		if (code >= 0x30 && code <= 0x39) digit = code - 0x30;
		else if (radix === 16 && code >= 0x41 && code <= 0x46) digit = code - 0x41 + 10;
		else if (radix === 16 && code >= 0x61 && code <= 0x66) digit = code - 0x61 + 10;
		else throw new ArchiveError(`Invalid CPIO archive: ${field} is not a valid base-${radix} number`);
		if (digit >= radix) throw new ArchiveError(`Invalid CPIO archive: ${field} is not a valid base-${radix} number`);
		value = value * radix + digit;
	}
	if (!Number.isSafeInteger(value)) throw new ArchiveError(`Invalid CPIO archive: ${field} is too large`);
	return value;
}

function parseHeader(bytes: Uint8Array, offset: number): ParsedHeader {
	requireRange(bytes, offset, offset + 2, "header");
	const first = bytes[offset]!;
	const second = bytes[offset + 1]!;
	if ((first === 0xc7 && second === 0x71) || (first === 0x71 && second === 0xc7)) {
		requireRange(bytes, offset, offset + BINARY_HEADER_SIZE, "old binary header");
		const littleEndian = first === 0xc7;
		const read16 = littleEndian ? readUInt16LE : readUInt16BE;
		const read32Words = (fieldOffset: number): number =>
			read16(bytes, offset + fieldOffset) * 0x10000 + read16(bytes, offset + fieldOffset + 2);
		return {

View on GitHub (pinned to 9690622007)

Solutions

  1. Regenerate the archive with a standard tool (cpio -H newc, or rpm2cpio for RPM payload) and re-download/verify checksums
  2. Verify you are feeding the right format — use sniffCpio on the buffer head before parsing
  3. Inspect the header bytes at the failing offset and confirm field encoding matches the format (hex for newc, octal for odc)
  4. If parsing RPM payloads, extract the cpio payload exactly (rpm2cpio) — do not pass raw RPM header bytes

Example fix

// before: parsing arbitrary bytes directly
const entries = await readCpio(source, options);
// after: sniff first and produce the archive with a standard format
const head = new Uint8Array(await source.slice(0, 6));
if (!sniffCpio(head)) throw new Error('not a supported CPIO archive');
const entries = await readCpio(source, options);
Defensive patterns

Strategy: validation

Validate before calling

import { sniffCpio } from '<lib>/ar/cpio';
const head = new Uint8Array(await source.slice(0, 110));
if (!sniffCpio(head)) throw new Error('input is not a supported CPIO stream');

Type guard

function isAsciiDigits(bytes: Uint8Array, radix: 8 | 16): boolean {
  return [...bytes].every(c =>
    (c >= 0x30 && c <= 0x37) ||
    (radix === 16 && ((c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66) || (c >= 0x38 && c <= 0x39))));
}

Try / catch

try {
  const entries = await readCpio(source, options);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('not a valid base-')) {
    // treat as corrupt/non-CPIO input: fall back to another format reader or reject
  } else throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/108caf73c7d7f5ff. Report an issue: GitHub.