can1357/oh-my-pi · error · ArchiveError
Invalid ${label} Huffman table: incomplete codes
Error message
Invalid ${label} Huffman table: incomplete codes What it means
After the length loop, CanonicalHuffman.build() requires the canonical codes to exactly fill the code space: firstCode[maximumLength] + counts[maximumLength] === 2^maximumLength. If the table is under-subscribed (unused code space remains), the tree would contain paths that decode to nothing, so the library rejects it. This catches archives whose declared code lengths do not form a complete Huffman code.
Source
Thrown at packages/utils/src/ar/lzh.ts:86
if (length > 16) throw new ArchiveError(`Invalid ${label} Huffman table: code is too long`);
if (length !== 0) {
counts[length]++;
maximumLength = Math.max(maximumLength, length);
}
}
if (maximumLength === 0) throw new ArchiveError(`Invalid ${label} Huffman table: no symbols`);
const nextCodes = new Uint32Array(17);
let code = 0;
for (let length = 1; length <= 16; length++) {
code = (code + counts[length - 1]!) * 2;
if (code + counts[length]! > 2 ** length) {
throw new ArchiveError(`Invalid ${label} Huffman table: oversubscribed codes`);
}
nextCodes[length] = code;
}
if (nextCodes[maximumLength]! + counts[maximumLength]! !== 2 ** maximumLength) {
throw new ArchiveError(`Invalid ${label} Huffman table: incomplete codes`);
}
const tree = new CanonicalHuffman(label);
for (let symbol = 0; symbol < symbolCount; symbol++) {
const length = lengths[symbol]!;
if (length === 0) continue;
const symbolCode = nextCodes[length]!;
nextCodes[length] = symbolCode + 1;
let node = 0;
for (let bitIndex = length - 1; bitIndex >= 0; bitIndex--) {
if (tree.#symbol[node]! >= 0) {
throw new ArchiveError(`Invalid ${label} Huffman table: prefix collision`);
}
const bit = (symbolCode >>> bitIndex) & 1;
let child = bit === 0 ? tree.#zero[node]! : tree.#one[node]!;
if (child < 0) {
child = tree.#symbol.length;
tree.#zero.push(-1);View on GitHub (pinned to 9690622007)
Solutions
- Re-extract or re-download the archive; compare file size and CRC
- Confirm you are using the matching decompress routine for the archive's method byte (LH5 vs LH6 vs LH7 differ in table sizes)
- If you are the encoder, emit lengths for every nonzero-frequency symbol so the code space is exactly filled
- Catch ArchiveError and report corruption instead of attempting partial decode
Example fix
// before: emitting only symbols actually used, leaving space unfilled writeLengths(freqs.filter(f => f > 0)); // after: use a complete-code canonical assignment writeLengths(canonicalCompleteLengths(freqs, 16)); // guarantees Kraft sum == 1
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the archive externally before decoding: // unlha t archive.lzh (exit 0 => structurally sound)
Try / catch
try {
const out = decompressLhStatic(data, size);
} catch (err) {
if (err instanceof ArchiveError && err.message.includes('incomplete codes')) {
throw new Error('archive code-length table is incomplete; file is corrupt');
}
throw err;
} Prevention
- Re-download corrupted archives rather than forcing extraction
- Match decompressor variant to the archive's method id
- If you are the encoder, guarantee a complete canonical code (Kraft sum == 1)
- Fail fast on first ArchiveError instead of partial decoding
When it happens
Trigger: Decompressing an LZH stream where the encoded symbol-count/length distribution is short by symbols — e.g. a truncated table section, an encoder that wrote fewer lengths than it declared, or corruption zeroing out trailing lengths in readTemporaryTree/readCommandTree/readPositionTree.
Common situations: Bit-flipped or truncated .lzh downloads, archives produced by buggy third-party compressors, mixing LH-format variants (LH5 table parsed as LH7), or fuzzing.
Related errors
- Invalid ${label} Huffman table: oversubscribed codes
- Invalid ${label} temporary Huffman table
- Invalid ${label} Huffman table: prefix collision
- Invalid ${label} Huffman table: duplicate code
- Invalid ${this.#label} Huffman code
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/eb8968b8a48b0eee.
Report an issue: GitHub.