can1357/oh-my-pi · error · ArchiveError

Invalid RPM package: truncated ${what} header

Error message

Invalid RPM package: truncated ${what} header

What it means

ArchiveError thrown by validateHeaderBody() when the header body byte length does not equal intro.bodySize computed from the intro — the header was declared with indexCount and dataSize but fewer (or more) bytes were actually read. Guards against truncated headers before index entries are interpreted.

Source

Thrown at packages/utils/src/ar/rpm.ts:74

	if (bytes.byteLength !== RPM_HEADER_INTRO_SIZE || readUInt32BE(bytes, 0) !== RPM_HEADER_MAGIC) {
		throw new ArchiveError(`Invalid RPM package: corrupt ${what} header magic`);
	}
	for (let offset = 4; offset < 8; offset++) {
		if (bytes[offset] !== 0) throw new ArchiveError(`Invalid RPM package: corrupt ${what} header reserved bytes`);
	}
	const indexCount = readUInt32BE(bytes, 8);
	const dataSize = readUInt32BE(bytes, 12);
	assertEntryCount(indexCount, options.limits);
	const indexSize = indexCount * RPM_INDEX_ENTRY_SIZE;
	const bodySize = indexSize + dataSize;
	if (!Number.isSafeInteger(bodySize)) throw new ArchiveError(`Invalid RPM package: ${what} header is too large`);
	assertIndexSize(RPM_HEADER_INTRO_SIZE + bodySize, options.limits, `RPM ${what} header`);
	return { indexCount, dataSize, bodySize, totalSize: RPM_HEADER_INTRO_SIZE + bodySize };
}

function validateHeaderBody(body: Uint8Array, intro: HeaderIntro, what: string): void {
	const indexSize = intro.indexCount * RPM_INDEX_ENTRY_SIZE;
	if (body.byteLength !== intro.bodySize) throw new ArchiveError(`Invalid RPM package: truncated ${what} header`);
	for (let index = 0; index < intro.indexCount; index++) {
		const recordOffset = index * RPM_INDEX_ENTRY_SIZE;
		const tag = readUInt32BE(body, recordOffset);
		const type = readUInt32BE(body, recordOffset + 4);
		const offset = readUInt32BE(body, recordOffset + 8);
		const count = readUInt32BE(body, recordOffset + 12);
		if (offset > intro.dataSize) throw new ArchiveError(`Invalid RPM package: tag ${tag} points outside header data`);
		const remaining = intro.dataSize - offset;
		let elementSize = 0;
		if (type === 1 || type === 2 || type === 7) elementSize = 1;
		else if (type === 3) elementSize = 2;
		else if (type === 4) elementSize = 4;
		else if (type === 5) elementSize = 8;
		else if (type === 0) {
			if (count !== 0) throw new ArchiveError(`Invalid RPM package: null tag ${tag} has values`);
			continue;
		} else if (type === RPM_TYPE_STRING || type === 8 || type === 9) {
			const stringCount = type === RPM_TYPE_STRING ? 1 : count;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download the package and verify size
  2. Check disk/network integrity for the source file
  3. Validate the file end-to-end (e.g. rpm -K) outside the library to confirm corruption

Example fix

// before
const blob = await response.blob();
await readRpmArchive(await blob.arrayBuffer());
// after
const buf = new Uint8Array(await response.arrayBuffer());
if (buf.byteLength !== expectedSize) throw new Error(`short download: ${buf.byteLength}/${expectedSize}`);
await readRpmArchive(buf);
Defensive patterns

Strategy: validation

Validate before calling

const expected = await fetchExpectedSizeFromRepoMetadata(url);
const actual = Bun.file(path).size;
if (actual !== expected) throw new Error(`incomplete download (${actual}/${expected})`);

Type guard

null

Try / catch

try {
  const rpm = await readRpmArchive(buf);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('truncated')) {
    // prompt re-download
  } else throw err;
}

Prevention

When it happens

Trigger: Signature or main header read where the body read came up short relative to the intro's declared bodySize — truncated file, or the source ends mid-header.

Common situations: Partially downloaded .rpm files, source streams cut off mid-header, corrupt mirrors.

Related errors


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