can1357/oh-my-pi · error · ArchiveError
Invalid archive: truncated data
Error message
Invalid archive: truncated data
What it means
After reading all bytes, readTar compares the byte count to the declared source size. A mismatch means the source delivered fewer bytes than promised — the archive is truncated. This is a hard structural failure because tar parsing cannot know how much trailing data was lost.
Source
Thrown at packages/utils/src/ar/tar.ts:588
storage: { type: "member", source: new TarMemberSource(buffer, dataOffset, sparse) },
});
}
if (!sawTerminator) throw new ArchiveError("Not a valid tar archive: missing terminating zero block");
resolvePendingLinks(entries, pendingLinks, limits);
return [...entries.values()];
}
/** Read and index a tar source after one bounded whole-stream read. */
export const readTar: FormatReader = async (source, options) => {
assertInMemorySize(source.size, options.limits);
let bytes: Uint8Array;
try {
bytes = await readAllBytes(source);
} catch (error) {
if (error instanceof ArchiveError) throw error;
throw new ArchiveError(error instanceof Error ? error.message : "Failed to read tar archive");
}
if (bytes.byteLength !== source.size) throw new ArchiveError("Invalid archive: truncated data");
return readTarEntriesFromBuffer(bytes, options);
};
/** Detect a tar header, including legacy pre-ustar archives, by its checksum. */
export function sniffTar(bytes: Uint8Array): boolean {
if (bytes.byteLength < BLOCK_SIZE) return false;
if (isZeroBlock(bytes, 0)) return true;
try {
if (readTarString(bytes, NAME_OFFSET, NAME_LENGTH).length === 0) return false;
const size = readTarSize(bytes, SIZE_OFFSET);
return Number.isSafeInteger(paddedSize(size)) && checksumMatches(bytes, 0);
} catch {
return false;
}
}
function writeField(target: Uint8Array, offset: number, length: number, value: Uint8Array): void {
if (value.byteLength > length) throw new ArchiveError("Tar header field is too long");View on GitHub (pinned to 9690622007)
Solutions
- Re-fetch/re-copy the archive and confirm the final byte length matches the expected size
- Do not pass a size value larger than the actual available data; compute size from the bytes you actually have
- Wait for the producing process/stream to finish (check file size stability or completion event) before reading
- If the size came from Content-Length, compare against the actual received length and treat mismatch as a failed download
Example fix
// before: trusting a potentially stale stat size
await readTar({ size: stat.size, read: () => stream });
// after: size = what was actually read
const bytes = new Uint8Array(await new Response(stream).arrayBuffer());
await readTar({ size: bytes.byteLength, read: async () => bytes }); Defensive patterns
Strategy: validation
Validate before calling
if (bytes.byteLength !== declaredSize) {
throw new Error(`archive incomplete: got ${bytes.byteLength} of ${declaredSize} bytes`);
} Try / catch
try {
return await readTar(source, options);
} catch (err) {
if (err instanceof ArchiveError && err.message === "Invalid archive: truncated data") {
// re-download / wait for producer to finish
}
throw err;
} Prevention
- Always derive source.size from the bytes actually received, not from headers like Content-Length
- Only read archives after the producing process/stream has signaled completion
- Compare downloaded size against expected checksum/size before parsing
When it happens
Trigger: Providing a tar source whose `size` metadata says N but whose stream yields fewer than N bytes — a size header taken from an HTTP Content-Length while the body was cut short, a stat() size on a file being concurrently truncated, or a source that reports pre-decompression size.
Common situations: Interrupted downloads with a Content-Length-driven progress bar, files still being written while read, mismatched size metadata in custom sources, gz not fully decompressed before being handed to readTar with the compressed size.
Related errors
- Archive member '${formatArchivePathForError(memberPath)}' is
- Archive member data is truncated
- Archive sparse metadata is truncated
- Archive member '${formatArchivePathForError(name)}' is trunc
- Invalid tar octal value: ${value}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/6211acef281fa247.
Report an issue: GitHub.