{"record":{"id":"2354e6af3224a566","repo":"can1357/oh-my-pi","slug":"invalid-or-corrupt-tar-archive-header","errorCode":null,"errorMessage":"Invalid or corrupt tar archive header","messagePattern":"Invalid or corrupt tar archive header","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/tar.ts","lineNumber":441,"sourceCode":"\t\tconst indexed = upsertArchiveEntry(entries, entry);\n\t\tif (!indexed) return;\n\t\tif (existing) pendingLinks.delete(existing);\n\t\tif (pendingLink) pendingLinks.set(indexed, pendingLink);\n\t\tassertEntryCount(entries.size, limits);\n\t};\n\tlet offset = 0;\n\tlet longName: string | undefined;\n\tlet longLink: string | undefined;\n\tlet localPax: Map<string, string> | undefined;\n\tconst globalPax = new Map<string, string>();\n\tlet sawTerminator = false;\n\n\twhile (offset + BLOCK_SIZE <= buffer.byteLength) {\n\t\tif (isZeroBlock(buffer, offset)) {\n\t\t\tsawTerminator = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (!checksumMatches(buffer, offset)) throw new ArchiveError(\"Invalid or corrupt tar archive header\");\n\t\tconst headerOffset = offset;\n\t\tconst typeFlag = String.fromCharCode(buffer[headerOffset + TYPEFLAG_OFFSET] || 0x30);\n\t\tlet size = readTarSize(buffer, headerOffset + SIZE_OFFSET);\n\t\tlet name = readTarString(buffer, headerOffset + NAME_OFFSET, NAME_LENGTH);\n\t\tif (isUstarHeader(buffer, headerOffset)) {\n\t\t\tconst prefix = readTarString(buffer, headerOffset + PREFIX_OFFSET, PREFIX_LENGTH);\n\t\t\tif (prefix) name = `${prefix}/${name}`;\n\t\t}\n\t\tlet linkName = readTarString(buffer, headerOffset + LINKNAME_OFFSET, LINKNAME_LENGTH);\n\t\tconst mtime = readTarNumeric(buffer, headerOffset + MTIME_OFFSET, MTIME_LENGTH);\n\t\tconst rawMode = readTarNumeric(buffer, headerOffset + MODE_OFFSET, MODE_LENGTH);\n\t\tconst mode = Number.isSafeInteger(rawMode) && rawMode >= 0 ? rawMode : undefined;\n\t\toffset += BLOCK_SIZE;\n\t\tconst dataBlocks = paddedSize(size);\n\t\tif (dataBlocks > buffer.byteLength - offset) throw new ArchiveError(\"Archive member data is truncated\");\n\t\tconst data = buffer.subarray(offset, offset + size);\n\n\t\tif (typeFlag === \"L\") {","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/tar.ts#L423-L459","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: raw gzipped bytes into tar reader\nconst bytes = await Bun.file(\"archive.tgz\").bytes();\nawait readTar(bytes, opts); // checksum failure\n// after: decompress first\nconst { gunzipSync } = await import(\"node:zlib\");\nconst bytes = gunzipSync(await Bun.file(\"archive.tgz\").bytes());\nawait readTar(bytes, opts);","handlingStrategy":"validation","validationCode":"import { sniffTar } from \"@oh-my-pi/pi-utils/ar/tar\";\nconst raw = await Bun.file(path).bytes();\n// Decompress if gzipped before handing bytes to the tar reader\nconst isGzip = raw[0] === 0x1f && raw[1] === 0x8b;\nconst bytes = isGzip ? gunzipSync(raw) : raw;\nif (!sniffTar(bytes)) throw new Error(`${path} is not a tar archive`);","typeGuard":"function isGzipBytes(b: Uint8Array): boolean {\n  return b.length >= 2 && b[0] === 0x1f && b[1] === 0x8b;\n}","tryCatchPattern":"try {\n  await readTar(bytes, opts);\n} catch (e) {\n  if (e instanceof ArchiveError && e.message === \"Invalid or corrupt tar archive header\") {\n    throw new Error(\"Not a valid (uncompressed) tar stream — decompress or re-download\");\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["archive","tar","checksum","corrupt-input"],"backgroundTag":"corrupt-archive-header","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}