can1357/oh-my-pi · error · ArchiveError

Invalid RPM package: truncated ${what}

Error message

Invalid RPM package: truncated ${what}

What it means

ArchiveError thrown by the RPM reader's readExact() helper when the requested byte range [start,end) is not a valid range within the source (non-safe integers, negative start, end<start, or end beyond source.size). This is the first of two truncation checks — it validates the range arithmetically before any I/O.

Source

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

	totalSize: number;
}

interface RpmMetadata {
	name?: string;
	version?: string;
	payloadFormat?: string;
	payloadCompressor?: string;
	payloadFlags?: string;
}

function align(value: number, alignment: number): number {
	const remainder = value % alignment;
	return remainder === 0 ? value : value + alignment - remainder;
}

async function readExact(source: ByteSource, start: number, end: number, what: string): Promise<Uint8Array> {
	if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || end > source.size) {
		throw new ArchiveError(`Invalid RPM package: truncated ${what}`);
	}
	const bytes = await source.read(start, end);
	if (bytes.byteLength !== end - start) throw new ArchiveError(`Invalid RPM package: truncated ${what}`);
	return bytes;
}

function parseHeaderIntro(bytes: Uint8Array, options: FormatReadOptions, what: string): HeaderIntro {
	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;

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download or re-fetch the .rpm file and verify size/checksum against the repository metadata
  2. Check that the byte source (file/blob) is fully written before parsing
  3. Confirm the file actually is an RPM (starts with the ar '!<arch>' / RPM lead) before parsing

Example fix

// before
const rpm = await readRpmArchive(await Bun.file(partialPath).arrayBuffer());
// after
const buf = await Bun.file(path).arrayBuffer();
if (buf.byteLength < 96) throw new Error(`RPM too small: ${buf.byteLength} bytes`);
const rpm = await readRpmArchive(buf);
Defensive patterns

Strategy: validation

Validate before calling

const size = Bun.file(path).size;
if (size < 96) throw new Error(`rpm too small (${size} bytes)`); // minimum: lead + headers

Type guard

null

Try / catch

try {
  const rpm = await readRpmArchive(buf);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes('truncated')) {
    // surface 'download incomplete' to the user
  } else throw err;
}

Prevention

When it happens

Trigger: Reading RPM structures (initial lead, signature header, main intro, body, payload) where computed offsets exceed the actual file size — i.e. the file is smaller than the RPM format requires at that point.

Common situations: Truncated downloads of .rpm files, HTTP transfers cut short, passing a non-RPM or empty file to the RPM reader, or CDN/proxy stripping bytes.

Related errors


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