FiloSottile/age · error
invalid character data part: s[%d]=%v
Error message
invalid character data part: s[%d]=%v
What it means
The characters after the separator must all belong to the bech32 charset (qpzry9x8gf2tvdw0s3jn54khce6mua7l, case-folded). Decode indexes the charset and reports the offending rune when IndexRune returns -1, e.g. digits like 1, b, i, o are excluded.
Source
Thrown at internal/bech32/bech32.go:165
pos := strings.LastIndex(s, "1")
if pos < 1 || pos+7 > len(s) {
return "", nil, fmt.Errorf("separator '1' at invalid position: pos=%d, len=%d", pos, len(s))
}
hrp = s[:pos]
for p, c := range hrp {
if c < 33 || c > 126 {
return "", nil, fmt.Errorf("invalid character human-readable part: s[%d]=%d", p, c)
}
}
for p, c := range s[pos+1:] {
// Fold ASCII explicitly. Unicode case folding can turn a non-ASCII
// rune into a shorter valid charset member.
if c >= 'A' && c <= 'Z' {
c += 'a' - 'A'
}
d := strings.IndexRune(charset, c)
if d == -1 {
return "", nil, fmt.Errorf("invalid character data part: s[%d]=%v", p, c)
}
data = append(data, byte(d))
}
if len(data) < 6 {
return "", nil, fmt.Errorf("data part too short")
}
if !verifyChecksum(hrp, data) {
return "", nil, fmt.Errorf("invalid checksum")
}
data, err = convertBits(data[:len(data)-6], 5, 8, false)
if err != nil {
return "", nil, err
}
return hrp, data, nil
}
View on GitHub (pinned to b74dce4cdb)
Solutions
- Fix the typo: replace excluded characters (b,i,o,1) with the intended bech32 character
- Re-copy the key programmatically instead of transcribing by hand
- Verify the input is actually bech32 and not base64/hex
Defensive patterns
Strategy: validation
Validate before calling
const charset1 = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
func validDataPart(s string) bool {
i := strings.LastIndex(s, "1")
if i < 0 { return false }
for _, c := range strings.ToLower(s[i+1:]) {
if !strings.ContainsRune(charset1, c) { return false }
}
return true
} Prevention
- Never hand-type keys; copy programmatically
- Remember bech32 excludes b, i, o and 1 from the data part
- Recognize base64/base58 by their alphabets and don't feed them to bech32 decoders
When it happens
Trigger: Decode with a data part containing '1', 'b', 'i', or 'o' or any non-charset character, e.g. "age1tbio..." typo or random base64 text.
Common situations: Hand-typing keys and confusing excluded characters (b/i, 0/o/1), pasting base64 or base58 strings that look bech32-like.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- illegal zero padding
- non-zero padding
- mixed case
- separator '1' at invalid position: pos=%d, len=%d
- invalid character human-readable part: s[%d]=%d
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/0d12ac4d5989ea95.
Report an issue: GitHub.