can1357/oh-my-pi · error · ArchiveError

Invalid RPM package: tag ${tag} string is not NUL-terminated

Error message

Invalid RPM package: tag ${tag} string is not NUL-terminated

What it means

When decoding a single-string metadata tag, the parser scans for the NUL terminator within the header data region; reaching the region end without one means the string is unterminated. Even though validateHeaderBody already checks termination, this second check guards the value-extraction path (e.g. signature vs main header divergence) before decoding.

Source

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

}

function readHeaderString(
	body: Uint8Array,
	indexSize: number,
	dataSize: number,
	offset: number,
	count: number,
	type: number,
	tag: number,
): string {
	if (type !== RPM_TYPE_STRING || count !== 1) {
		throw new ArchiveError(`Invalid RPM package: tag ${tag} must contain one string`);
	}
	const start = indexSize + offset;
	const limit = indexSize + dataSize;
	let end = start;
	while (end < limit && body[end] !== 0) end++;
	if (end === limit) throw new ArchiveError(`Invalid RPM package: tag ${tag} string is not NUL-terminated`);
	if (end - start > 4096) throw new ArchiveError(`Invalid RPM package: tag ${tag} string is too large`);
	try {
		return UTF8_FATAL_DECODER.decode(body.subarray(start, end));
	} catch {
		throw new ArchiveError(`Invalid RPM package: tag ${tag} is not valid UTF-8`);
	}
}

function parseMainHeader(body: Uint8Array, intro: HeaderIntro): RpmMetadata {
	validateHeaderBody(body, intro, "main");
	const indexSize = intro.indexCount * RPM_INDEX_ENTRY_SIZE;
	if (body.byteLength !== intro.bodySize) throw new ArchiveError("Invalid RPM package: truncated main header");
	const metadata: RpmMetadata = {};
	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);

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-download or rebuild the .rpm; the header data region is damaged.
  2. Verify truncation by comparing file size with the published checksum.
  3. Cross-check the header with `rpm -qp` to confirm corruption.
  4. Catch ArchiveError and reject/skip the package in your processing loop.
Defensive patterns

Strategy: try-catch

Validate before calling

if (fileSize < manifestSize) throw new Error("download incomplete");

Try / catch

try {
  const entries = await readRpm(source, options);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("not NUL-terminated")) {
    return reDownloadAndRetry(file); // truncation is the usual cause
  }
  throw err;
}

Prevention

When it happens

Trigger: parseMainHeader calls readHeaderString for tag 1000/1001/1124/1125/1126 and the bytes from the tag offset to the end of the header data contain no 0 byte — typically because the header is truncated or its data size field is wrong.

Common situations: Incomplete downloads cutting off the header tail; corruption of the final NUL byte; headers rebuilt by tooling that miscounts dataSize.

Related errors


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