gchq/CyberChef · error · OperationError

Invalid witness version: ${witnessVersion}. Must be 0-16.

Error message

Invalid witness version: ${witnessVersion}. Must be 0-16.

What it means

Thrown by encode() in src/core/lib/Bech32.mjs:193 in SegWit mode (segwit=true, data.length>=2) when the first byte of data — interpreted as the witness version — exceeds 16. BIP-0173/BIP-0350 define witness versions 0 through 16 only; each version is a single 5-bit word, so values 17-255 are not representable and not defined by the protocol.

Source

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

    }

    // Check HRP characters (ASCII 33-126)
    for (let i = 0; i < hrp.length; i++) {
        const c = hrp.charCodeAt(i);
        if (c < 33 || c > 126) {
            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);
    }

View on GitHub (pinned to 4290ea7539)

Solutions

  1. Ensure data[0] is a witness version in 0..16 when segwit=true.
  2. If you do not mean SegWit, call encode with segwit=false (the default).
  3. Prepend the correct witness version byte, e.g. [0, ...witnessProgram] for v0.
  4. Validate before calling: if (segwit && (data[0] < 0 || data[0] > 16)) throw.

Example fix

// before - raw program passed without a witness version, or version > 16
encode('bc', programBytes, 'Bech32', true);

// after - prepend witness version 0
encode('bc', [0, ...programBytes], 'Bech32', true);
// or, for generic (non-SegWit) Bech32
encode('bc', programBytes, 'Bech32', false);
Defensive patterns

Strategy: validation

Validate before calling

function validateSegwitPayload(data) {
  if (!Array.isArray(data) || data.length < 2) throw new Error('SegWit payload too short');
  if (data[0] < 0 || data[0] > 16) throw new RangeError(`witness version ${data[0]} out of range 0-16`);
  return data;
}

Type guard

function isValidSegwitVersion(b) {
  return Number.isInteger(b) && b >= 0 && b <= 16;
}

Try / catch

try {
  encode('bc', data, 'Bech32', true);
} catch (e) {
  if (e instanceof OperationError && /Invalid witness version/.test(e.message)) {
    // data[0] is not a version; use segwit=false or prepend a real version
  }
}

Prevention

When it happens

Trigger: encode('bc', [17, ...program], 'Bech32', true), encode('bc', [255, ...], 'Bech32', true), or any segwit encode where data[0] > 16. Common when the caller did not actually intend SegWit but left segwit=true, so a raw data byte (e.g. 0x1c) becomes the 'witness version'.

Common situations: Passing a full raw payload as SegWit data without prepending a witness version; using segwit=true for a non-SegWit (generic) Bech32 use; witness version sourced from a byte that was actually data; protocol upgrade where the caller assumed a version > 16 exists.

Related errors


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