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

  1. Dump the '// ' string-table member and check its byte length against offsets referenced by member names
  2. Regenerate the archive with GNU ar so the table and offsets are rebuilt consistently
  3. Fix offset computation in custom writers (offsets are 0-based byte positions into the table, in emission order)
  4. 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

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


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