{"record":{"id":"9ba9ff893bf348d5","repo":"can1357/oh-my-pi","slug":"invalid-ar-archive-member-header","errorCode":null,"errorMessage":"Invalid ar archive member header","messagePattern":"Invalid ar archive member header","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/unix-ar.ts","lineNumber":87,"sourceCode":"function parseRequiredSize(value: string): number {\n\tconst parsed = parseOptionalNumber(value, 10, \"member size\");\n\tif (parsed === undefined) throw new ArchiveError(\"Invalid ar archive member size\");\n\treturn parsed;\n}\n\nfunction parseHeader(header: Uint8Array): {\n\trawName: string;\n\tphysicalSize: number;\n\tmtimeSeconds?: number;\n\tmode?: number;\n\tbsdNameLength?: number;\n} {\n\tif (\n\t\theader.byteLength !== HEADER_SIZE ||\n\t\theader[HEADER_TRAILER_OFFSET] !== 0x60 ||\n\t\theader[HEADER_TRAILER_OFFSET + 1] !== 0x0a\n\t) {\n\t\tthrow new ArchiveError(\"Invalid ar archive member header\");\n\t}\n\tconst rawName = decodeAsciiField(header, 0, NAME_SIZE);\n\tconst mtimeSeconds = parseOptionalNumber(decodeAsciiField(header, 16, 12), 10, \"modification time\");\n\tparseOptionalNumber(decodeAsciiField(header, 28, 6), 10, \"user id\");\n\tparseOptionalNumber(decodeAsciiField(header, 34, 6), 10, \"group id\");\n\tconst mode = parseOptionalNumber(decodeAsciiField(header, 40, 8), 8, \"mode\");\n\tconst physicalSize = parseRequiredSize(decodeAsciiField(header, 48, 10));\n\tlet bsdNameLength: number | undefined;\n\tif (rawName.startsWith(\"#1/\")) {\n\t\tconst encodedLength = rawName.slice(3);\n\t\tif (!/^\\d+$/.test(encodedLength)) throw new ArchiveError(\"Invalid ar archive BSD extended name length\");\n\t\tbsdNameLength = Number.parseInt(encodedLength, 10);\n\t\tif (!Number.isSafeInteger(bsdNameLength) || bsdNameLength <= 0 || bsdNameLength > physicalSize) {\n\t\t\tthrow new ArchiveError(\"Invalid ar archive BSD extended name length\");\n\t\t}\n\t}\n\treturn { rawName, physicalSize, mtimeSeconds, mode, bsdNameLength };\n}","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/unix-ar.ts#L69-L105","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: writer forgets alignment padding\nawait out.write(memberData); // odd size -> next header misaligned\n// after: pad to even\nif (memberData.byteLength % 2 === 1) await out.write(new Uint8Array([0x0a]));","handlingStrategy":"validation","validationCode":"function looksLikeAr(data: Uint8Array): boolean {\n  return new TextDecoder().decode(data.subarray(0, 8)) === '!<arch>\\n';\n}\nfunction isArHeader(header: Uint8Array): boolean {\n  return header.byteLength === 60 && header[58] === 0x60 && header[59] === 0x0a;\n}\n// check global magic before parsing; check trailer at each header boundary","typeGuard":"function isArHeader(b: Uint8Array): boolean {\n  return b.byteLength === 60 && b[58] === 0x60 && b[59] === 0x0a;\n}","tryCatchPattern":"try {\n  const archive = await parseAr(file);\n} catch (err) {\n  if (err instanceof ArchiveError && err.message === 'Invalid ar archive member header') {\n    throw new Error(`${file} is not a valid ar archive`, { cause: err });\n  } else throw err;\n}","preventionTips":["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"],"tags":["archive","parsing","ar-format","corrupt-file"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}