can1357/oh-my-pi · error · ArchiveError

Compress (.Z) output exceeds the ${this.#limit}-byte limit

Error message

Compress (.Z) output exceeds the ${this.#limit}-byte limit

What it means

BoundedOutput caps decompressed output at a caller-supplied limit to prevent zip-bomb style memory exhaustion. #ensure throws when writing `additional` bytes would push the total past that limit (or overflow a safe integer). The limit is defensive, not a stream property — the input may be valid but simply decompresses larger than allowed.

Source

Thrown at packages/utils/src/ar/codecs/lzw.ts:81

		this.#limit = limit;
		this.#bytes = new Uint8Array(Math.min(limit, Math.max(64, Math.min(inputSize * 2, 64 * 1024))));
	}

	appendReversed(stack: Uint8Array, length: number): void {
		this.#ensure(length);
		for (let index = length - 1; index >= 0; index--) {
			this.#bytes[this.#length++] = stack[index]!;
		}
	}

	finish(): Uint8Array {
		return this.#bytes.slice(0, this.#length);
	}

	#ensure(additional: number): void {
		const needed = this.#length + additional;
		if (!Number.isSafeInteger(needed) || needed > this.#limit) {
			throw new ArchiveError(`Compress (.Z) output exceeds the ${this.#limit}-byte limit`);
		}
		if (needed <= this.#bytes.byteLength) {
			return;
		}
		let capacity = Math.max(needed, Math.min(this.#limit, Math.max(64, this.#bytes.byteLength * 2)));
		if (capacity > this.#limit) {
			capacity = this.#limit;
		}
		const grown = new Uint8Array(capacity);
		grown.set(this.#bytes.subarray(0, this.#length));
		this.#bytes = grown;
	}
}

function decode(bytes: Uint8Array, maxOutput: number): Uint8Array {
	if (!Number.isSafeInteger(maxOutput) || maxOutput < 0) {
		throw new ArchiveError("Invalid compress (.Z) output limit");
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Raise maxOutput to a value ≥ the true decompressed size (known file size, or a generous upper bound).
  2. If the source is untrusted, keep a limit but stream/process in chunks instead of removing the cap entirely.
  3. Confirm you aren't passing the compressed size as the output limit.
  4. If the data is untrusted and limit removal is unacceptable, treat the error as a decompression-bomb signal and reject the input.

Example fix

// before
lzwDecompress(bytes, bytes.byteLength);
// after: output can be larger than input; use a real bound
lzwDecompress(bytes, 256 * 1024 * 1024);
Defensive patterns

Strategy: validation

Validate before calling

// Know the decompressed size before decoding, or pick a generous bound
const maxOutput = knownUncompressedSize ?? 256 * 1024 * 1024;
if (!Number.isSafeInteger(maxOutput) || maxOutput < 0) throw new Error("Invalid maxOutput");

Try / catch

try {
  return lzwDecompress(bytes, maxOutput);
} catch (err) {
  if (err instanceof ArchiveError && err.message.includes("-byte limit")) {
    throw new Error(".Z payload exceeds output cap — possible decompression bomb or undersized limit", { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling lzwDecompress(bytes, maxOutput) where the .Z stream's decompressed size exceeds maxOutput; decoding an untrusted, highly compressible input with a small limit.

Common situations: Decompressing untrusted uploads with a conservative cap; legacy .Z files larger than a hard-coded limit; accidentally passing bytes.byteLength instead of the expected output size as maxOutput.

Related errors


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