FiloSottile/age · error
invalid data range: data[%d]=%d (frombits=%d)
Error message
invalid data range: data[%d]=%d (frombits=%d)
What it means
This error comes from the internal bech32 convertBits helper, which regroups data between bit widths (e.g. 8-bit bytes to 5-bit groups). Each input element must fit in frombits bits; if any value has bits set above that width, the conversion is invalid and this error is returned. It is used by Encode (bytes to 5-bit, always valid) and Decode (5-bit to bytes) paths.
Source
Thrown at internal/bech32/bech32.go:86
values := append(hrpExpand(hrp), data...)
values = append(values, []byte{0, 0, 0, 0, 0, 0}...)
mod := polymod(values) ^ 1
ret := make([]byte, 6)
for p := range ret {
shift := 5 * (5 - p)
ret[p] = byte(mod>>shift) & 31
}
return ret
}
func convertBits(data []byte, frombits, tobits byte, pad bool) ([]byte, error) {
var ret []byte
acc := uint32(0)
bits := byte(0)
maxv := byte(1<<tobits - 1)
for idx, value := range data {
if value>>frombits != 0 {
return nil, fmt.Errorf("invalid data range: data[%d]=%d (frombits=%d)", idx, value, frombits)
}
acc = acc<<frombits | uint32(value)
bits += frombits
for bits >= tobits {
bits -= tobits
ret = append(ret, byte(acc>>bits)&maxv)
}
}
if pad {
if bits > 0 {
ret = append(ret, byte(acc<<(tobits-bits))&maxv)
}
} else if bits >= frombits {
return nil, fmt.Errorf("illegal zero padding")
} else if byte(acc<<(tobits-bits))&maxv != 0 {
return nil, fmt.Errorf("non-zero padding")
}
return ret, nilView on GitHub (pinned to b74dce4cdb)
Solutions
- Since this is an internal package, do not call internal/bech32.convertBits directly with arbitrary-width data; always pass values that fit in frombits bits
- If decoding, treat the input string as invalid — bech32.Decode surfaces this as a decode failure; verify the string against the original source
- For Encode, 8→5 conversion cannot trigger this; if you see it, your data slice was corrupted before encoding — check the buffer source
- Add a pre-check `for i, v := range data { if v >= 1<<frombits { ... } }` in any custom caller to reject bad input early
Defensive patterns
Strategy: validation
Validate before calling
func validateBech32Data(data []byte, frombits uint) bool {
for _, v := range data {
if int(v) >= 1<<frombits {
return false
}
}
return true
} Prevention
- Do not call internal/bech32 directly from application code; use the public age APIs
- Always source 5-bit data from Decode output, never from hand-built slices
- Fuzz/property-test any custom bech32 usage with bounded-value generators
- Validate decoded strings end-to-end (checksum + padding) before trusting payloads
When it happens
Trigger: Calling Decode on a bech32 string whose data part contains 5-bit values >= 256 when regrouping to 8 bits (i.e. value>>8 != 0 with frombits=5 can't happen, so this triggers when convertBits is called with frombits < bit-width of supplied data) — practically, when internal code or tests invoke convertBits/Encode-Decode paths with out-of-range data, such as decoding a string whose checksum passed but data groups exceed the target range in the 5→8 direction.
Common situations: Corrupted bech32 strings that happen to pass checksum checks elsewhere; hand-rolled code calling the internal bech32 package directly with raw byte values wider than frombits; fuzz tests or property tests generating invalid 5-bit payloads.
Related errors
- illegal zero padding
- non-zero padding
- invalid HRP: %q
- invalid HRP character: hrp[%d]=%d
- mixed case HRP: %q
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/96d1ef5822c54862.
Report an issue: GitHub.