can1357/oh-my-pi · error · ArchiveError
Invalid ${this.#label} Huffman code: excessive depth
Error message
Invalid ${this.#label} Huffman code: excessive depth What it means
decode() also enforces a hard depth cap of 16 bits (LZH's maximum code length). If no leaf is reached after consuming 16 bits, the code is longer than any the table permits, so the stream cannot correspond to this table. This is a safety net that also prevents infinite loops on pathological/corrupt data.
Source
Thrown at packages/utils/src/ar/lzh.ts:128
node = child;
}
if (tree.#symbol[node]! >= 0 || tree.#zero[node]! >= 0 || tree.#one[node]! >= 0) {
throw new ArchiveError(`Invalid ${label} Huffman table: duplicate code`);
}
tree.#symbol[node] = symbol;
}
return tree;
}
decode(reader: MsbBitReader): number {
let node = 0;
for (let depth = 0; depth <= 16; depth++) {
const symbol = this.#symbol[node]!;
if (symbol >= 0) return symbol;
node = reader.read(1) === 0 ? this.#zero[node]! : this.#one[node]!;
if (node < 0) throw new ArchiveError(`Invalid ${this.#label} Huffman code`);
}
throw new ArchiveError(`Invalid ${this.#label} Huffman code: excessive depth`);
}
}
function readCodeLength(reader: MsbBitReader, label: string): number {
let length = reader.read(3);
if (length === 7) {
while (reader.read(1) !== 0) {
length++;
if (length > 16) throw new ArchiveError(`Invalid ${label} Huffman table: code is too long`);
}
}
return length;
}
function readTemporaryTree(reader: MsbBitReader, label: string): CanonicalHuffman {
const symbolCount = 19;
const encodedCount = reader.read(5);
if (encodedCount === 0) return CanonicalHuffman.single(reader.read(5), symbolCount, label);View on GitHub (pinned to 9690622007)
Solutions
- Treat the archive as corrupt: verify externally and re-obtain a clean copy
- Recheck where the table section ends and the data section begins (9-bit command count / 5-bit temp count boundaries)
- Do not retry on the same bytes; ArchiveError here is deterministic
- If you generate archives, cap code lengths at 16 bits (package-merge) so stream and table always agree
Defensive patterns
Strategy: try-catch
Try / catch
try {
const out = decompressLhStatic(data, size);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes('excessive depth')) {
throw new Error('archive stream/table mismatch (corrupt data)');
}
throw err;
} Prevention
- Validate archives externally before processing
- Cap encoder code lengths at 16 bits so stream and table always agree
- Confirm table/data boundary offsets before decoding
- Quarantine files that hit the depth cap — they are corrupt or adversarial
When it happens
Trigger: Corrupt data bits forming a never-terminating path, or a table built with max length < 16 while the data contains longer patterns — always a stream/table mismatch, seen via decompressLhStatic or temporary-tree decoding on damaged input.
Common situations: Severely corrupted or scrambled archives, wrong-offset parsing where data bytes are actually still table bytes, fuzzed inputs.
Related errors
- Invalid ${this.#label} Huffman code
- Invalid ${label} Huffman table: oversubscribed codes
- Invalid ${label} Huffman table: incomplete codes
- Invalid ${label} Huffman table: prefix collision
- Invalid ${label} Huffman table: duplicate code
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5b032614ca80fa06.
Report an issue: GitHub.