can1357/oh-my-pi · error · ArchiveError

Invalid tar sparse real size

Error message

Invalid tar sparse real size

What it means

When a PAX record key is GNU.sparse.realsize (or GNU.sparse.size), its decimal value must fit within MAX_PAX_NUMERIC_BYTES (32) bytes. This throw fires when the sparse-file real-size value in a PAX header is longer than that cap — a value absurd for any real file, so the parser rejects it as corrupt.

Source

Thrown at packages/utils/src/ar/tar.ts:205

			if (byte < 0x30 || byte > 0x39) throw new ArchiveError("Invalid tar PAX record");
			length = length * 10 + (byte - 0x30);
			if (length > data.byteLength - pos) throw new ArchiveError("Invalid tar PAX record");
		}
		if (length <= 0 || pos + length > data.byteLength || data[pos + length - 1] !== 0x0a) {
			throw new ArchiveError("Invalid tar PAX record");
		}
		const record = data.subarray(space + 1, pos + length - 1);
		const equals = record.indexOf(0x3d);
		if (equals >= 0) {
			const key = record.subarray(0, equals);
			const value = record.subarray(equals + 1);
			if (bytesMatchAscii(key, 0, PAX_SPARSE_MARKER)) {
				attrs.set(PAX_SPARSE_MARKER, value.byteLength === 0 ? "" : "1");
				if (bytesEqualAscii(key, "GNU.sparse.name")) {
					attrs.set("GNU.sparse.name", readPaxPath(value, "PAX sparse path", limits));
				} else if (bytesEqualAscii(key, "GNU.sparse.realsize") || bytesEqualAscii(key, "GNU.sparse.size")) {
					if (value.byteLength > MAX_PAX_NUMERIC_BYTES) {
						throw new ArchiveError("Invalid tar sparse real size");
					}
					attrs.set("GNU.sparse.realsize", TEXT_DECODER.decode(value));
				}
			} else if (bytesEqualAscii(key, "path") || bytesEqualAscii(key, "linkpath")) {
				const field = bytesEqualAscii(key, "path") ? "PAX path" : "PAX link target";
				attrs.set(field === "PAX path" ? "path" : "linkpath", readPaxPath(value, field, limits));
			} else if (bytesEqualAscii(key, "size")) {
				if (value.byteLength > MAX_PAX_NUMERIC_BYTES) throw new ArchiveError("Invalid tar member size");
				attrs.set("size", TEXT_DECODER.decode(value));
			}
		}
		pos += length;
	}
	return attrs;
}

function applyGlobalPax(globalPax: Map<string, string>, update: ReadonlyMap<string, string>): void {
	for (const [key, value] of update) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the archive with GNU tar (sparse archives are the least portable tar variant) and regenerate if corrupt.
  2. Avoid GNU sparse format when interoperability matters: repack with `tar --sparse=never` or plain storage.
  3. If writing sparse PAX headers yourself, ensure realsize values are plain decimal and realistically bounded.
  4. Catch ArchiveError and reject the archive; sparse members cannot be read by this library anyway (TarMemberSource throws for sparse data).

Example fix

// before
$ tar cf sparse.tar --sparse=foo.bin
// after
$ tar cf foo.tar --sparse=never foo.bin
Defensive patterns

Strategy: fallback

Validate before calling

// GNU sparse PAX realsize must be decimal and <= 32 bytes long
function validSparseRealsize(value: string): boolean {
  return value.length <= 32 && /^\d+$/.test(value);
}

Try / catch

try {
  const entries = readTarEntriesFromBuffer(buffer, options);
} catch (err) {
  if (err instanceof ArchiveError && err.message === "Invalid tar sparse real size") {
    throw new Error("Archive declares an impossible GNU sparse realsize; archive is corrupt or hostile");
  }
  throw err;
}

Prevention

When it happens

Trigger: Reading a tar containing a GNU sparse member whose PAX extended header has `GNU.sparse.realsize=<33+ digit string>` — parsed by parsePaxRecords inside readTarEntriesFromBuffer.

Common situations: Corrupted sparse archives, maliciously crafted sparse headers claiming impossible sizes, or archives produced by a broken sparse-aware writer.

Related errors


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