can1357/oh-my-pi · error · ArchiveError
Invalid or corrupt tar archive header
Error message
Invalid or corrupt tar archive header
What it means
Thrown when a 512-byte tar block's stored checksum does not match the computed unsigned or signed byte sum of the header. The library validates every non-zero header block before parsing, so any bit corruption or non-tar data in the stream fails here.
Source
Thrown at packages/utils/src/ar/tar.ts:441
const indexed = upsertArchiveEntry(entries, entry);
if (!indexed) return;
if (existing) pendingLinks.delete(existing);
if (pendingLink) pendingLinks.set(indexed, pendingLink);
assertEntryCount(entries.size, limits);
};
let offset = 0;
let longName: string | undefined;
let longLink: string | undefined;
let localPax: Map<string, string> | undefined;
const globalPax = new Map<string, string>();
let sawTerminator = false;
while (offset + BLOCK_SIZE <= buffer.byteLength) {
if (isZeroBlock(buffer, offset)) {
sawTerminator = true;
break;
}
if (!checksumMatches(buffer, offset)) throw new ArchiveError("Invalid or corrupt tar archive header");
const headerOffset = offset;
const typeFlag = String.fromCharCode(buffer[headerOffset + TYPEFLAG_OFFSET] || 0x30);
let size = readTarSize(buffer, headerOffset + SIZE_OFFSET);
let name = readTarString(buffer, headerOffset + NAME_OFFSET, NAME_LENGTH);
if (isUstarHeader(buffer, headerOffset)) {
const prefix = readTarString(buffer, headerOffset + PREFIX_OFFSET, PREFIX_LENGTH);
if (prefix) name = `${prefix}/${name}`;
}
let linkName = readTarString(buffer, headerOffset + LINKNAME_OFFSET, LINKNAME_LENGTH);
const mtime = readTarNumeric(buffer, headerOffset + MTIME_OFFSET, MTIME_LENGTH);
const rawMode = readTarNumeric(buffer, headerOffset + MODE_OFFSET, MODE_LENGTH);
const mode = Number.isSafeInteger(rawMode) && rawMode >= 0 ? rawMode : undefined;
offset += BLOCK_SIZE;
const dataBlocks = paddedSize(size);
if (dataBlocks > buffer.byteLength - offset) throw new ArchiveError("Archive member data is truncated");
const data = buffer.subarray(offset, offset + size);
if (typeFlag === "L") {View on GitHub (pinned to 9690622007)
Solutions
- Verify you decompressed first: gunzip .tar.gz/.tgz before passing bytes, or use the composite reader that sniffs compression.
- Verify the file with `tar tf archive.tar` or `gzip -t` to confirm integrity; re-download if corrupt.
- Check you are reading the correct file/offset (e.g. not an inner slice of a larger file).
- Sniff format before parsing (the library's sniffTar exists for this) and route non-tar input to the right reader.
Example fix
// before: raw gzipped bytes into tar reader
const bytes = await Bun.file("archive.tgz").bytes();
await readTar(bytes, opts); // checksum failure
// after: decompress first
const { gunzipSync } = await import("node:zlib");
const bytes = gunzipSync(await Bun.file("archive.tgz").bytes());
await readTar(bytes, opts); Defensive patterns
Strategy: validation
Validate before calling
import { sniffTar } from "@oh-my-pi/pi-utils/ar/tar";
const raw = await Bun.file(path).bytes();
// Decompress if gzipped before handing bytes to the tar reader
const isGzip = raw[0] === 0x1f && raw[1] === 0x8b;
const bytes = isGzip ? gunzipSync(raw) : raw;
if (!sniffTar(bytes)) throw new Error(`${path} is not a tar archive`); Type guard
function isGzipBytes(b: Uint8Array): boolean {
return b.length >= 2 && b[0] === 0x1f && b[1] === 0x8b;
} Try / catch
try {
await readTar(bytes, opts);
} catch (e) {
if (e instanceof ArchiveError && e.message === "Invalid or corrupt tar archive header") {
throw new Error("Not a valid (uncompressed) tar stream — decompress or re-download");
}
throw e;
} Prevention
- Always decompress .tar.gz/.tgz/.bz2 before plain tar parsing.
- Verify downloads (checksum or `gzip -t`) before indexing.
- Sniff the format with sniffTar before calling readTar.
- Transfer archives in binary mode to avoid byte corruption.
When it happens
Trigger: Calling readTar/readTarEntriesFromBuffer on data whose header checksum fails: truncated/corrupted downloads, files that are not actually tar (e.g. wrong decompression — a gzip served as plain tar), or concatenation damage mid-archive.
Common situations: Forgetting to decompress (.tar.gz passed directly to a plain tar reader), partial HTTP downloads, transferring archives in text mode, or reading the wrong file/offset.
Related errors
- Invalid XZ stream: footer CRC32 mismatch
- Invalid XZ stream: header CRC32 mismatch
- Invalid old-GNU name record
- Invalid tar octal value: ${value}
- Truncated embedded addon archive entry: ${filename}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/2354e6af3224a566.
Report an issue: GitHub.