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, nil

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. 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
  2. If decoding, treat the input string as invalid — bech32.Decode surfaces this as a decode failure; verify the string against the original source
  3. For Encode, 8→5 conversion cannot trigger this; if you see it, your data slice was corrupted before encoding — check the buffer source
  4. 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

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


AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31). Data as JSON: /api/errors/96d1ef5822c54862. Report an issue: GitHub.