gchq/CyberChef · error · OperationError

Invalid witness program length: ${witnessProgram.length}. Mu

Error message

Invalid witness program length: ${witnessProgram.length}. Must be 2-40 bytes.

What it means

Thrown by encode() in src/core/lib/Bech32.mjs:199 in SegWit mode when the witness program (data after the first byte) length is less than 2 or greater than 40 bytes. BIP-0141/BIP-0173 constrain witness programs to 2-40 bytes; outside this range the program cannot be a valid SegWit output and the address would be meaningless.

Source

Thrown at src/core/lib/Bech32.mjs:199

            throw new OperationError(`HRP contains invalid character at position ${i}. Only printable ASCII characters (33-126) are allowed.`);
        }
    }

    // Convert HRP to lowercase
    const hrpLower = hrp.toLowerCase();

    let words;
    if (segwit && data.length >= 2) {
        // SegWit encoding: first byte is witness version (0-16), rest is witness program
        const witnessVersion = data[0];
        if (witnessVersion > 16) {
            throw new OperationError(`Invalid witness version: ${witnessVersion}. Must be 0-16.`);
        }
        const witnessProgram = Array.prototype.slice.call(data, 1);

        // Validate witness program length per BIP-0141
        if (witnessProgram.length < 2 || witnessProgram.length > 40) {
            throw new OperationError(`Invalid witness program length: ${witnessProgram.length}. Must be 2-40 bytes.`);
        }
        if (witnessVersion === 0 && witnessProgram.length !== 20 && witnessProgram.length !== 32) {
            throw new OperationError(`Invalid witness program length for v0: ${witnessProgram.length}. Must be 20 or 32 bytes.`);
        }

        // Witness version is kept as single 5-bit value, program is converted
        words = [witnessVersion].concat(toWords(witnessProgram));
    } else {
        // Generic encoding: convert all bytes to 5-bit words
        words = toWords(data);
    }

    // Create checksum
    const checksum = createChecksum(hrpLower, words, encoding);

    // Build result string
    let result = hrpLower + "1";
    for (const w of words.concat(checksum)) {

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Verify the witness program is a HASH160 (20 bytes) or SHA256 (32 bytes) for v0, or 2-40 bytes for v1+.
  2. Re-derive the program from its source (e.g. HASH160 of the compressed pubkey for P2WPKH).
  3. Confirm data[0] is the version and data.slice(1) is exactly the program — not the other way around.
  4. If not building a SegWit address, use segwit=false.

Example fix

// before - program too short
encode('bc', [0, 0xab], 'Bech32', true);

// after - 20-byte v0 program (P2WPKH)
encode('bc', [0, ...hash160.slice(0, 20)], 'Bech32', true);
Defensive patterns

Strategy: validation

Validate before calling

function validateWitnessProgram(program) {
  if (!Array.isArray(program) || program.length < 2 || program.length > 40) {
    throw new RangeError(`witness program length ${program ? program.length : 0} out of range 2-40`);
  }
  return program;
}

Type guard

function isValidWitnessProgramLength(bytes) {
  return Array.isArray(bytes) && bytes.length >= 2 && bytes.length <= 40;
}

Try / catch

try {
  encode('bc', [version, ...program], 'Bech32', true);
} catch (e) {
  if (e instanceof OperationError && /witness program length/.test(e.message)) {
    // re-derive the program from its source hash
  }
}

Prevention

When it happens

Trigger: encode('bc', [0, <1 byte>], 'Bech32', true) — program length 1 < 2. encode('bc', [0, ...41 bytes], 'Bech32', true) — length 41 > 40. Any segwit encode where data.slice(1).length is outside [2, 40].

Common situations: Truncated program (missing bytes during copy-paste); concatenating the wrong fields so the program is too short; using a 1-byte program by mistake; witness program from a buggy key derivation that produced an oversized output.

Related errors


AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13). Data as JSON: /api/errors/7373d2030d02dca2. Report an issue: GitHub.