can1357/oh-my-pi · error · ArchiveError

Invalid ar archive ${field}

Error message

Invalid ar archive ${field}

What it means

This ArchiveError is thrown by parseOptionalNumber in packages/utils/src/ar/unix-ar.ts when a numeric header field (mode, mtime, uid, gid) contains characters invalid for the expected radix — octal fields must match /^[0-7]+$/, decimal /^\d+$/, and empty or '-1' (the GNU 'unknown' sentinel) is treated as absent. So this throw means the field is present but malformed. The library throws rather than guessing so corrupt archives are rejected deterministically.

Source

Thrown at packages/utils/src/ar/unix-ar.ts:63

	}
}

function decodeAsciiField(bytes: Uint8Array, offset: number, length: number): string {
	let end = offset + length;
	while (end > offset && bytes[end - 1] === 0x20) end--;
	let value = "";
	for (let index = offset; index < end; index++) {
		const byte = bytes[index]!;
		if (byte < 0x20 || byte > 0x7e) throw new ArchiveError("Invalid ar archive header field");
		value += String.fromCharCode(byte);
	}
	return value;
}

function parseOptionalNumber(value: string, radix: 8 | 10, field: string): number | undefined {
	if (value === "" || value === "-1") return undefined;
	const pattern = radix === 8 ? /^[0-7]+$/ : /^\d+$/;
	if (!pattern.test(value)) throw new ArchiveError(`Invalid ar archive ${field}`);
	const parsed = Number.parseInt(value, radix);
	if (!Number.isSafeInteger(parsed)) throw new ArchiveError(`Invalid ar archive ${field}`);
	return parsed;
}

function parseRequiredSize(value: string): number {
	const parsed = parseOptionalNumber(value, 10, "member size");
	if (parsed === undefined) throw new ArchiveError("Invalid ar archive member size");
	return parsed;
}

function parseHeader(header: Uint8Array): {
	rawName: string;
	physicalSize: number;
	mtimeSeconds?: number;
	mode?: number;
	bsdNameLength?: number;
} {

View on GitHub (pinned to 9690622007)

Solutions

  1. Hex-dump the 60-byte member header and verify fields 16-27 (mtime), 28-33 (uid), 34-39 (gid), 40-47 (mode) hold only ASCII digits (0-7 for mode), space-padded
  2. Regenerate the archive with a standard tool (ar, llvm-ar) instead of custom writer code
  3. If you control the writer, space-pad numeric fields to their fixed width
  4. Re-download or restore the archive if corruption in transit is suspected

Example fix

// before: sign-padded mode field
'-644    '
// after: valid octal, space-padded
'644     '
Defensive patterns

Strategy: validation

Validate before calling

const dec = new TextDecoder();
function checkNumericField(header: Uint8Array, start: number, len: number, radix: 8 | 10): boolean {
  const s = dec.decode(header.subarray(start, start + len)).trim();
  return s === '' || s === '-1' || (radix === 8 ? /^[0-7]+$/.test(s) : /^\d+$/.test(s));
}
// before parsing: mtime, uid, gid, mode slots must all pass
const ok = checkNumericField(header, 16, 12, 10) && checkNumericField(header, 28, 6, 10)
  && checkNumericField(header, 34, 6, 10) && checkNumericField(header, 40, 8, 8);

Type guard

function isArNumericField(value: string, radix: 8 | 10): boolean {
  return value === '' || value === '-1' || (radix === 8 ? /^[0-7]+$/.test(value) : /^\d+$/.test(value));
}

Try / catch

import { ArchiveError } from '@oh-my-pi/pi-utils';
try {
  await archive.list();
} catch (err) {
  if (err instanceof ArchiveError && err.message.startsWith('Invalid ar archive')) {
    // quarantine file, fall back to raw-bytes inspection
  } else throw err;
}

Prevention

When it happens

Trigger: Parsing an ar member whose 6-byte uid, 6-byte gid, 8-byte mode, or 12-byte mtime header slots contain non-digit bytes (non-padding spaces, letters, or negatives other than '-1') via parseHeader, reached through any archive listing/extraction API.

Common situations: Hand-edited or truncated .a/.deb archives; broken custom writers that use sign characters or zero-padding instead of space-padding; decimal values written into the octal mode slot; corrupted downloads.

Related errors


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