gchq/CyberChef · error · OperationError
Invalid padding: non-zero bits in padding
Error message
Invalid padding: non-zero bits in padding
What it means
Thrown by fromWords() in src/core/lib/Bech32.mjs:155 when, after consuming all 5-bit words into bytes, between 1 and 4 residual bits remain and at least one of them is non-zero. BIP-0173 requires padding bits to be zero; a non-zero residual means the encoding is non-canonical (the same data could be represented more than one way) and is treated as invalid.
Source
Thrown at src/core/lib/Bech32.mjs:155
value = (value << 5) | words[i];
bits += 5;
while (bits >= 8) {
bits -= 8;
result.push((value >> bits) & 255);
}
}
// Check for invalid padding per BIP-0173
// Condition 1: Cannot have 5+ bits remaining (would indicate incomplete byte)
if (bits >= 5) {
throw new OperationError("Invalid padding: too many bits remaining");
}
// Condition 2: Remaining padding bits must all be zero
if (bits > 0) {
const paddingValue = (value << (8 - bits)) & 255;
if (paddingValue !== 0) {
throw new OperationError("Invalid padding: non-zero bits in padding");
}
}
return result;
}
/**
* Encode data to Bech32/Bech32m string
*
* @param {string} hrp - Human-readable part
* @param {number[]|Uint8Array} data - Data bytes to encode
* @param {string} encoding - "Bech32" or "Bech32m"
* @param {boolean} segwit - If true, treat first byte as witness version (for Bitcoin SegWit)
* @returns {string} - Encoded Bech32/Bech32m string
*/
export function encode(hrp, data, encoding = "Bech32", segwit = false) {
// Validate HRP
if (!hrp || hrp.length === 0) {View on GitHub (pinned to 4290ea7539)
Solutions
- Treat this as data corruption: re-obtain the Bech32 string from its source.
- If you control encoding, ensure your toWords equivalent zero-pads the final word (this library's toWords at line 119-121 already does).
- Validate the checksum first; non-zero padding almost always coincides with checksum failure.
- Avoid hand-editing individual characters of a Bech32 string.
Example fix
// before - last word has non-zero padding bits
const bytes = fromWords([0x1f, 0x1f, 0x1f, 0x10]); // residual bits may be non-zero
// after - use canonical encoding via toWords/encode round-trip
const encoded = encode('bc', originalBytes, 'Bech32');
const { data } = decode(encoded); Defensive patterns
Strategy: try-catch
Validate before calling
function wordsHaveZeroPadding(words) {
let value = 0, bits = 0;
for (const w of words) { value = (value << 5) | w; bits += 5; while (bits >= 8) { bits -= 8; } }
if (bits === 0) return true;
return ((value << (8 - bits)) & 255) === 0;
} Type guard
function isCanonicalBech32Words(words) {
return Array.isArray(words) && wordsHaveZeroPadding(words);
} Try / catch
try {
const bytes = fromWords(words);
} catch (e) {
if (e instanceof OperationError && /non-zero bits in padding/.test(e.message)) {
// non-canonical encoding; treat as corrupt
}
} Prevention
- Only accept Bech32 produced by a canonical encoder (this library's toWords zero-pads).
- Never hand-edit individual characters of a Bech32 string.
- Pair this check with checksum verification to confirm integrity.
When it happens
Trigger: fromWords(words) where the residual 1-4 bits encode a non-zero value — e.g. words ending in a value whose low bits are set beyond the last byte boundary. Reached internally by decode() for strings like a hand-edited Bech32 whose last data word has stray low bits, or via direct fromWords call on non-canonical input.
Common situations: A character in the data part was altered (corruption) producing a word whose residual bits are non-zero; a non-canonical encoder that did not zero-pad; test vectors from an implementation that ignores BIP-0173's canonical-padding rule.
Related errors
- Invalid padding: too many bits remaining
- Invalid Bech32 string: exceeds maximum length of 90 characte
- Invalid Bech32 string: mixed case is not allowed. Use all up
- Error: Base64 padding character (${pad}) not used in the cor
- Error: Base64 not padded to a multiple of 4.
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/be2ad4bd45fd97b0.
Report an issue: GitHub.