FiloSottile/age · error

invalid HRP character: hrp[%d]=%d

Error message

invalid HRP character: hrp[%d]=%d

What it means

Bech32 restricts HRP characters to the printable US-ASCII range 33–126. bech32.Encode scans every rune of the HRP and rejects any character outside this range. This guarantees the encoded string contains only valid bech32 characters.

Source

Thrown at internal/bech32/bech32.go:119

	} else if byte(acc<<(tobits-bits))&maxv != 0 {
		return nil, fmt.Errorf("non-zero padding")
	}
	return ret, nil
}

// Encode encodes the HRP and a bytes slice to Bech32. If the HRP is uppercase,
// the output will be uppercase.
func Encode(hrp string, data []byte) (string, error) {
	values, err := convertBits(data, 8, 5, true)
	if err != nil {
		return "", err
	}
	if len(hrp) < 1 {
		return "", fmt.Errorf("invalid HRP: %q", hrp)
	}
	for p, c := range hrp {
		if c < 33 || c > 126 {
			return "", fmt.Errorf("invalid HRP character: hrp[%d]=%d", p, c)
		}
	}
	if strings.ToUpper(hrp) != hrp && strings.ToLower(hrp) != hrp {
		return "", fmt.Errorf("mixed case HRP: %q", hrp)
	}
	lower := strings.ToLower(hrp) == hrp
	hrp = strings.ToLower(hrp)
	var ret strings.Builder
	ret.WriteString(hrp)
	ret.WriteString("1")
	for _, p := range values {
		ret.WriteByte(charset[p])
	}
	for _, p := range createChecksum(hrp, values) {
		ret.WriteByte(charset[p])
	}
	if lower {
		return ret.String(), nil

View on GitHub (pinned to b74dce4cdb)

Solutions

  1. Sanitize the HRP before encoding: strip whitespace/control characters with strings.TrimSpace and strings.Map, or reject non-ASCII input at the source
  2. Verify the HRP source encoding — read files as UTF-8/ASCII and trim line endings (strings.TrimRight(s, "\r\n"))
  3. For age usage, use the fixed constants ("age", "AGE-SECRET-KEY-") instead of deriving the HRP from variable input

Example fix

// before
hrp := string(prefixBytes) // may contain "\n" or non-ASCII
s, err := bech32.Encode(hrp, data) // "invalid HRP character"

// after
hrp := strings.TrimSpace(string(prefixBytes))
for _, c := range hrp {
    if c < 33 || c > 126 {
        return fmt.Errorf("HRP contains invalid character %q", c)
    }
}
s, err := bech32.Encode(hrp, data)
Defensive patterns

Strategy: validation

Validate before calling

func validHRPChars(hrp string) bool {
	for _, c := range hrp {
		if c < 33 || c > 126 { return false }
	}
	return true
}

Prevention

When it happens

Trigger: Calling bech32.Encode with an HRP containing control characters, spaces (< 33), high bytes or non-ASCII Unicode (> 126) — e.g. an HRP built from user input, a mis-decoded byte slice, or a prefix with a trailing newline.

Common situations: HRP values read from files or argv that include a trailing "\n"; strings decoded from UTF-16 or Latin-1 sources producing bytes > 126; user-supplied prefixes containing spaces or emoji; log-formatted strings accidentally used as HRPs.

Related errors


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