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

  1. Check the member's declared size in the header for corruption (huge or negative values) — re-obtain a known-good archive
  2. Process very large tar archives via fileByteSource/streaming instead of buffering fully in memory
  3. Cap member sizes with archive limits (maxMemberSize) before extraction so oversized declares are rejected up front
  4. 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

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


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