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
- Verify the file starts with the global magic '!<arch>\n'
- Hex-dump around the failing offset and check for the 0x60 0x0a header trailer
- Fix member size fields in the producer — an off-by-one size desynchronizes every subsequent header
- Emit a padding byte after odd-sized member data to maintain 2-byte alignment
- 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
- Confirm files start with '!<arch>\n' before parsing
- Keep member data 2-byte aligned (pad odd-size members with a newline byte)
- Make member size fields exact; a wrong size desynchronizes all later headers
- Detect file type by magic (zip/tar/ar) before dispatching to the ar parser
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
- Invalid ar archive member size
- Empty ar archive long name at offset ${offset}
- Ar archive member '${name}' references a missing long-name t
- Invalid ARJ ${field}: missing terminator
- Invalid ARJ basic header size
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/9ba9ff893bf348d5.
Report an issue: GitHub.