can1357/oh-my-pi · error · ArchiveError
Unterminated ar archive long name at offset ${offset}
Error message
Unterminated ar archive long name at offset ${offset} What it means
resolveLongName scans the string table from the given offset for a terminator (NUL or newline); if it reaches the end of the table without finding one, it throws 'Unterminated ar archive long name at offset <n>'. GNU ar terminates every table entry with '/\n' (or '/' + NUL), so a missing terminator means the '// ' table is malformed or truncated mid-entry.
Source
Thrown at packages/utils/src/ar/unix-ar.ts:137
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,
source: ByteSource,
options: FormatReadOptions,
): ArchiveIndexEntry[] {
const entries = new Map<string, ArchiveIndexEntry>();View on GitHub (pinned to 9690622007)
Solutions
- Ensure the producer appends '/\n' after every name in the '// ' table, including the last one
- Verify the string-table member's size field includes all terminators and pads the table to even size
- Regenerate the archive with GNU ar / llvm-ar
- Compare the declared table size against actual table bytes to locate the truncation
Example fix
// before: last name not terminated
table = Buffer.concat([table, Buffer.from('lastName')]);
// after: terminate every entry
for (const n of names) table = Buffer.concat([table, Buffer.from(n + '/\n')]); Defensive patterns
Strategy: try-catch
Validate before calling
function stringTableWellFormed(table: Uint8Array): boolean {
// every GNU entry ends with '/' followed by \n or NUL
if (table.byteLength === 0) return true;
const last = table[table.byteLength - 1];
return last === 0x0a || last === 0x00;
}
// verify on the '// ' member payload before parsing references Type guard
function isTerminatedArStringTable(table: Uint8Array): boolean {
return table.byteLength === 0 || table[table.byteLength - 1] === 0x0a || table[table.byteLength - 1] === 0x00;
} Try / catch
try {
const entries = await archive.list();
} catch (err) {
if (err instanceof ArchiveError && err.message.startsWith('Unterminated ar archive long name')) {
// truncated string table: re-fetch/rebuild archive; parse offset from message for diagnostics
} else throw err;
} Prevention
- Terminate every table entry with '/\n', including the final one
- Include terminators in the table member's size field
- Pad the table to even byte length
- Verify archive integrity (size/hash) after download before parsing
When it happens
Trigger: Parsing an archive whose string table's final entry lacks its trailing terminator because the '// ' member was truncated by exactly the terminator bytes, or a custom writer forgot to append '/\n' after the last name.
Common situations: Truncated downloads cutting the tail of the archive; custom writers concatenating names without separators; tools that strip trailing newlines from archive members.
Related errors
- Invalid ar archive member name '${reference}'
- Invalid ar archive long-name offset '${reference}'
- 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/2e2f30a86c8c1c2a.
Report an issue: GitHub.