can1357/oh-my-pi · error · ArchiveError

Invalid ar archive: truncated member header

Error message

Invalid ar archive: truncated member header

What it means

While scanning members, fewer than HEADER_SIZE (60) bytes remained before end-of-input, so a complete member header could not be read. The library treats a header that cannot be fully present as a structurally invalid archive.

Source

Thrown at packages/utils/src/ar/unix-ar.ts:202

		assertEntryCount(entries.size, options.limits);
	}
	ensureParentDirectories(entries, options.limits);
	return [...entries.values()];
}

function readSignatureFromBuffer(bytes: Uint8Array): void {
	if (!sniffUnixAr(bytes)) throw new ArchiveError("Invalid ar archive signature");
}

/** Parse a fully materialized Unix ar archive for composition by formats such as deb. */
export function readUnixArEntriesFromBuffer(bytes: Uint8Array, options: FormatReadOptions): ArchiveIndexEntry[] {
	readSignatureFromBuffer(bytes);
	const records: RawArMember[] = [];
	let longNames: Uint8Array | undefined;
	let metadataSize = 0;
	for (let position = SIGNATURE.length; position < bytes.byteLength; ) {
		if (bytes.byteLength - position < HEADER_SIZE)
			throw new ArchiveError("Invalid ar archive: truncated member header");
		const header = parseHeader(readMemoryRange(bytes, position, position + HEADER_SIZE));
		metadataSize += HEADER_SIZE;
		assertIndexSize(metadataSize, options.limits, "index");
		const payloadOffset = position + HEADER_SIZE;
		const payloadEnd = payloadOffset + header.physicalSize;
		if (!Number.isSafeInteger(payloadEnd) || payloadEnd > bytes.byteLength) {
			throw new ArchiveError("Invalid ar archive: truncated member data");
		}
		let name = header.rawName;
		let nameByteLength = Buffer.byteLength(name, "utf-8");
		let dataOffset = payloadOffset;
		let size = header.physicalSize;
		if (header.bsdNameLength !== undefined) {
			metadataSize += header.bsdNameLength;
			assertIndexSize(metadataSize, options.limits, "index");
			const nameBytes = readMemoryRange(bytes, payloadOffset, payloadOffset + header.bsdNameLength);
			const decoded = decodeBsdName(nameBytes, options.limits);
			name = decoded.name;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-acquire the archive and verify its size/hash; the file is truncated.
  2. If the source is a stream, ensure the whole file was written before parsing.
  3. Re-create the archive with ar; test it with `ar t` first.
  4. If you must read partially downloaded archives, pre-check `bytes.byteLength` sanity or use range requests to fetch complete data.

Example fix

// before: parsing a partially downloaded file
const entries = await readUnixAr(partialBuffer);
// after: confirm completeness
if (actualSize !== expectedSize) throw new Error('incomplete download');
const entries = await readUnixAr(fullBuffer);
Defensive patterns

Strategy: validation

Validate before calling

if (bytes.byteLength < 8) throw new Error('not an ar archive');
// tail safety: ensure any trailing data is a multiple-free but header-sized or absent
const tail = bytes.byteLength - 8;
if (tail > 0 && tail < 60 && !allowTrailingGarbage) throw new Error('truncated trailing header');

Try / catch

try {
  return await readUnixArEntriesFromBuffer(bytes, opts);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('truncated member header')) {
    throw new Error('archive incomplete: re-download and verify checksum');
  }
  throw err;
}

Prevention

When it happens

Trigger: Trailing garbage under 60 bytes, or a truncated archive whose last member header is cut off mid-way; the loop position advances past payloadEnd and the remaining tail is shorter than 60 bytes.

Common situations: Incomplete downloads (FTP/HTTP cut short); archives appended with a partial member; disk-full during archive creation; a file that had the signature but almost no members.

Related errors


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