can1357/oh-my-pi · error · ArchiveError
Invalid ar archive long-name offset '${reference}'
Error message
Invalid ar archive long-name offset '${reference}' What it means
In resolveLongName, the '/<offset>' reference parsed to a valid number but the offset is not a safe integer, is negative, or points at or beyond the end of the '// ' long-name string table. The offset must index an existing byte in the table for a name to be recovered, so out-of-range references are rejected as 'Invalid ar archive long-name offset'.
Source
Thrown at packages/utils/src/ar/unix-ar.ts:133
if (nameBytes.byteLength === 0) throw new ArchiveError("Invalid ar archive empty BSD extended name");
return { name: decodeName(nameBytes, limits), byteLength: nameBytes.byteLength };
}
function shortName(rawName: string): string {
if (rawName === "/" || rawName === "//" || rawName === "/SYM64/") return rawName;
return rawName.endsWith("/") ? rawName.slice(0, -1) : rawName;
}
function resolveLongName(
reference: string,
table: Uint8Array,
limits: ArchiveLimits,
): { name: string; byteLength: number } {
const offsetText = reference.slice(1);
if (!/^\d+$/.test(offsetText)) throw new ArchiveError(`Invalid ar archive member name '${reference}'`);
const offset = Number.parseInt(offsetText, 10);
if (!Number.isSafeInteger(offset) || offset < 0 || offset >= table.byteLength) {
throw new ArchiveError(`Invalid ar archive long-name offset '${reference}'`);
}
let end = offset;
while (end < table.byteLength && table[end] !== 0 && table[end] !== 0x0a) end++;
if (end === table.byteLength) throw new ArchiveError(`Unterminated ar archive long name at offset ${offset}`);
let nameEnd = end;
if (table[end] === 0x0a && nameEnd > offset && table[nameEnd - 1] === 0x2f) nameEnd--;
if (nameEnd === offset) throw new ArchiveError(`Empty ar archive long name at offset ${offset}`);
const nameBytes = table.subarray(offset, nameEnd);
return { name: decodeName(nameBytes, limits), byteLength: nameBytes.byteLength };
}
function isMetadataName(name: string): boolean {
return name === "/" || name === "//" || name === "/SYM64/" || name === "__.SYMDEF" || name === "__.SYMDEF SORTED";
}
function materializeEntries(
records: RawArMember[],
longNames: Uint8Array | undefined,View on GitHub (pinned to 9690622007)
Solutions
- Dump the '// ' string-table member and check its byte length against offsets referenced by member names
- Regenerate the archive with GNU ar so the table and offsets are rebuilt consistently
- Fix offset computation in custom writers (offsets are 0-based byte positions into the table, in emission order)
- Check the table member wasn't lost in a concatenate/split operation
Example fix
// before: offset past end of table nameField = '/' + String(table.byteLength) // >= table.byteLength // after: offset of where the name was appended const offset = table.byteLength; table = Buffer.concat([table, Buffer.from(name + '/\n')]); nameField = '/' + String(offset);
Defensive patterns
Strategy: validation
Validate before calling
function longNameOffsetInRange(rawName: string, tableSize: number): boolean {
if (!/^\/\d+$/.test(rawName)) return false;
const off = Number.parseInt(rawName.slice(1), 10);
return Number.isSafeInteger(off) && off >= 0 && off < tableSize;
}
// parse the '// ' member size first, then validate each '/'-reference Type guard
function isInRangeLongNameOffset(rawName: string, tableSize: number): boolean {
const off = Number.parseInt(rawName.slice(1), 10);
return Number.isSafeInteger(off) && off >= 0 && off < tableSize;
} Try / catch
try {
const files = await archive.list();
} catch (err) {
if (err instanceof ArchiveError && err.message.startsWith('Invalid ar archive long-name offset')) {
// string table missing/truncated relative to references — regenerate archive
} else throw err;
} Prevention
- Always emit the '// ' table before members that reference it
- Recompute offsets whenever table entries are added or removed
- Never concatenate ar archives by naive byte appending (offsets break)
- Validate offsets against the table size in the writer before finalizing
When it happens
Trigger: Parsing a GNU long-name member whose name is '/999999' while the string table is far smaller; the '// ' table member truncated or missing so table.byteLength is smaller than the referenced offsets; corruption of either the name field or the table.
Common situations: Archives where the '// ' table was dropped or truncated by a processing tool (e.g. naive concatenation of archives); off-by-one offset computation in custom writers; fuzzed inputs.
Related errors
- Invalid ar archive member name '${reference}'
- Unterminated ar archive long name at offset ${offset}
- Invalid ar archive ${field}
- Invalid ar archive member size
- Invalid ar archive member header
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/890141a18811318a.
Report an issue: GitHub.