can1357/oh-my-pi · error · ArchiveError
Archive member '${formatArchivePathForError(memberPath)}' ha
Error message
Archive member '${formatArchivePathForError(memberPath)}' has an invalid size What it means
After slicing the member bytes, TarMemberSource.read() confirms the subarray length equals the requested size. This is a final invariant check — if a subarray within a valid buffer somehow returns fewer bytes (offset arithmetic overflow near Uint8Array length boundaries, or size larger than Number-safe limits interacting with subarray clamping), the read is rejected rather than returning mis-sized data. In practice it is a defensive backstop behind the truncation check at line 73.
Source
Thrown at packages/utils/src/ar/tar.ts:78
constructor(buffer: Uint8Array, dataOffset: number, sparse: boolean) {
this.#buffer = buffer;
this.#dataOffset = dataOffset;
this.#sparse = sparse;
}
async read(size: number, memberPath: string): Promise<Uint8Array> {
if (this.#sparse) {
throw new ArchiveError(
`Archive member '${formatArchivePathForError(memberPath)}' is a sparse file and cannot be read`,
);
}
if (size > this.#buffer.byteLength - this.#dataOffset) {
throw new ArchiveError(`Archive member '${formatArchivePathForError(memberPath)}' is truncated`);
}
const bytes = this.#buffer.subarray(this.#dataOffset, this.#dataOffset + size);
if (bytes.byteLength !== size) {
throw new ArchiveError(`Archive member '${formatArchivePathForError(memberPath)}' has an invalid size`);
}
return bytes;
}
}
function readTarString(buffer: Uint8Array, offset: number, length: number): string {
const limit = Math.min(offset + length, buffer.byteLength);
let end = offset;
while (end < limit && buffer[end] !== 0) end++;
return TEXT_DECODER.decode(buffer.subarray(offset, end));
}
function bytesEqualAscii(bytes: Uint8Array, value: string): boolean {
return bytes.byteLength === value.length && bytesMatchAscii(bytes, 0, value);
}
function isUstarHeader(buffer: Uint8Array, offset: number): boolean {
return (View on GitHub (pinned to 9690622007)
Solutions
- Check the member's declared size in the header for corruption (huge or negative values) — re-obtain a known-good archive
- Process very large tar archives via fileByteSource/streaming instead of buffering fully in memory
- Cap member sizes with archive limits (maxMemberSize) before extraction so oversized declares are rejected up front
- Report/fix if a custom code path passes a computed size inconsistent with the header (off-by-one, wrong units)
Example fix
// before: reading member with unchecked header-declared size
const size = readMemberSize(header);
const data = await member.read(size, path);
// after: validate the declared size against limits first
const size = readMemberSize(header);
if (!Number.isSafeInteger(size) || size < 0 || size > limits.maxMemberSize) {
throw new Error(`implausible member size ${size} for ${path}`);
}
const data = await member.read(size, path); Defensive patterns
Strategy: validation
Validate before calling
const declared = readMemberSize(header);
if (!Number.isSafeInteger(declared) || declared < 0 || declared > limits.maxMemberSize) {
throw new Error(`corrupt tar header: implausible member size ${declared}`);
} Type guard
function hasPlausibleSize(size: unknown): size is number {
return typeof size === "number" && Number.isSafeInteger(size) && size >= 0;
} Try / catch
try {
const data = await entry.source.read(entry.size, entry.path);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes("invalid size")) {
throw new Error(`member ${entry.path} has a corrupt or overflowing size field; archive is untrustworthy`, { cause: err });
}
throw err;
} Prevention
- Enforce archive limits (maxMemberSize) so oversized header declares are rejected before read()
- Sanity-check parsed tar size fields for safe-integer range and sign before use
- For very large archives, prefer file-backed sources over full in-memory buffers to avoid typed-array limits
- Verify archive checksums — this error usually indicates a corrupted size field rather than a caller bug
When it happens
Trigger: A read(size, path) call where dataOffset + size exceeds Uint8Array indexing limits or otherwise causes subarray() to clamp: sizes beyond the buffer's max safe range, or arithmetic producing a end offset past the typed array's length — caught only after the line-73 pre-check passed.
Common situations: Archives with extremely large members near typed-array limits (multi-GB in-memory tar buffers); a size field parsed via BigInt path that overflows Number precision; corrupted headers producing sizes that pass the coarse check but overflow the slice arithmetic.
Related errors
- Invalid tar octal value: ${value}
- Truncated embedded addon archive entry: ${filename}
- Archive member '${formatArchivePathForError(memberPath)}' is
- Invalid tar numeric field
- Unsafe embedded addon archive entry: ${filename}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/23f264071ad6cb8c.
Report an issue: GitHub.