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(), nilView on GitHub (pinned to b74dce4cdb)
Solutions
- Sanitize the HRP before encoding: strip whitespace/control characters with strings.TrimSpace and strings.Map, or reject non-ASCII input at the source
- Verify the HRP source encoding — read files as UTF-8/ASCII and trim line endings (strings.TrimRight(s, "\r\n"))
- 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
- Trim whitespace and line endings from any HRP read from files or argv
- Restrict HRP inputs to ASCII at the API boundary; reject Unicode early
- Read config files with explicit UTF-8 handling to avoid mis-decoded high bytes
- Use fixed age constants instead of user-supplied HRPs wherever possible
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
- invalid HRP: %q
- mixed case HRP: %q
- invalid data range: data[%d]=%d (frombits=%d)
- illegal zero padding
- non-zero padding
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/c40a447fbf855e60.
Report an issue: GitHub.