can1357/oh-my-pi · error · ArchiveError

Invalid ar archive member header

Error message

Invalid ar archive member header

What it means

Thrown by parseHeader when the 60-byte member header is structurally invalid: its length is not HEADER_SIZE, or the magic trailer at bytes 58-59 is not the backtick-newline pair (0x60 0x0a) required by the ar format. This is the first structural check on every member header, so it usually means the parser lost synchronization with the file layout (headers are 60 bytes; member data is 2-byte aligned).

Source

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

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;
} {
	if (
		header.byteLength !== HEADER_SIZE ||
		header[HEADER_TRAILER_OFFSET] !== 0x60 ||
		header[HEADER_TRAILER_OFFSET + 1] !== 0x0a
	) {
		throw new ArchiveError("Invalid ar archive member header");
	}
	const rawName = decodeAsciiField(header, 0, NAME_SIZE);
	const mtimeSeconds = parseOptionalNumber(decodeAsciiField(header, 16, 12), 10, "modification time");
	parseOptionalNumber(decodeAsciiField(header, 28, 6), 10, "user id");
	parseOptionalNumber(decodeAsciiField(header, 34, 6), 10, "group id");
	const mode = parseOptionalNumber(decodeAsciiField(header, 40, 8), 8, "mode");
	const physicalSize = parseRequiredSize(decodeAsciiField(header, 48, 10));
	let bsdNameLength: number | undefined;
	if (rawName.startsWith("#1/")) {
		const encodedLength = rawName.slice(3);
		if (!/^\d+$/.test(encodedLength)) throw new ArchiveError("Invalid ar archive BSD extended name length");
		bsdNameLength = Number.parseInt(encodedLength, 10);
		if (!Number.isSafeInteger(bsdNameLength) || bsdNameLength <= 0 || bsdNameLength > physicalSize) {
			throw new ArchiveError("Invalid ar archive BSD extended name length");
		}
	}
	return { rawName, physicalSize, mtimeSeconds, mode, bsdNameLength };
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the file starts with the global magic '!<arch>\n'
  2. Hex-dump around the failing offset and check for the 0x60 0x0a header trailer
  3. Fix member size fields in the producer — an off-by-one size desynchronizes every subsequent header
  4. Emit a padding byte after odd-sized member data to maintain 2-byte alignment
  5. Regenerate/recover the archive with standard tooling

Example fix

// before: writer forgets alignment padding
await out.write(memberData); // odd size -> next header misaligned
// after: pad to even
if (memberData.byteLength % 2 === 1) await out.write(new Uint8Array([0x0a]));
Defensive patterns

Strategy: validation

Validate before calling

function looksLikeAr(data: Uint8Array): boolean {
  return new TextDecoder().decode(data.subarray(0, 8)) === '!<arch>\n';
}
function isArHeader(header: Uint8Array): boolean {
  return header.byteLength === 60 && header[58] === 0x60 && header[59] === 0x0a;
}
// check global magic before parsing; check trailer at each header boundary

Type guard

function isArHeader(b: Uint8Array): boolean {
  return b.byteLength === 60 && b[58] === 0x60 && b[59] === 0x0a;
}

Try / catch

try {
  const archive = await parseAr(file);
} catch (err) {
  if (err instanceof ArchiveError && err.message === 'Invalid ar archive member header') {
    throw new Error(`${file} is not a valid ar archive`, { cause: err });
  } else throw err;
}

Prevention

When it happens

Trigger: Any parse/list/extract call where the bytes at a supposed header boundary don't end with '`\n' — the file isn't an ar archive at all, a member's declared size is wrong so the next header read lands mid-data, or alignment padding was skipped.

Common situations: Passing a non-ar file (tar, zip, plain text) to the parser; truncated or corrupted .deb/.a files; custom writers forgetting the 1-byte pad after odd-sized members; reading at the wrong offset after a size-field bug.

Related errors


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