{"record":{"id":"0d12ac4d5989ea95","repo":"FiloSottile/age","slug":"invalid-character-data-part-s-d-v","errorCode":null,"errorMessage":"invalid character data part: s[%d]=%v","messagePattern":"invalid character data part: s\\[(.+?)\\]=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bech32/bech32.go","lineNumber":165,"sourceCode":"\tpos := strings.LastIndex(s, \"1\")\n\tif pos < 1 || pos+7 > len(s) {\n\t\treturn \"\", nil, fmt.Errorf(\"separator '1' at invalid position: pos=%d, len=%d\", pos, len(s))\n\t}\n\thrp = s[:pos]\n\tfor p, c := range hrp {\n\t\tif c < 33 || c > 126 {\n\t\t\treturn \"\", nil, fmt.Errorf(\"invalid character human-readable part: s[%d]=%d\", p, c)\n\t\t}\n\t}\n\tfor p, c := range s[pos+1:] {\n\t\t// Fold ASCII explicitly. Unicode case folding can turn a non-ASCII\n\t\t// rune into a shorter valid charset member.\n\t\tif c >= 'A' && c <= 'Z' {\n\t\t\tc += 'a' - 'A'\n\t\t}\n\t\td := strings.IndexRune(charset, c)\n\t\tif d == -1 {\n\t\t\treturn \"\", nil, fmt.Errorf(\"invalid character data part: s[%d]=%v\", p, c)\n\t\t}\n\t\tdata = append(data, byte(d))\n\t}\n\tif len(data) < 6 {\n\t\treturn \"\", nil, fmt.Errorf(\"data part too short\")\n\t}\n\tif !verifyChecksum(hrp, data) {\n\t\treturn \"\", nil, fmt.Errorf(\"invalid checksum\")\n\t}\n\tdata, err = convertBits(data[:len(data)-6], 5, 8, false)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\treturn hrp, data, nil\n}\n","sourceCodeStart":147,"sourceCodeEnd":181,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/bech32/bech32.go#L147-L181","documentation":"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.","triggerScenarios":"Decode with a data part containing '1', 'b', 'i', or 'o' or any non-charset character, e.g. \"age1tbio...\" typo or random base64 text.","commonSituations":"Hand-typing keys and confusing excluded characters (b/i, 0/o/1), pasting base64 or base58 strings that look bech32-like.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const charset1 = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\"\nfunc validDataPart(s string) bool {\n    i := strings.LastIndex(s, \"1\")\n    if i < 0 { return false }\n    for _, c := range strings.ToLower(s[i+1:]) {\n        if !strings.ContainsRune(charset1, c) { return false }\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["bech32","charset","decoding"],"backgroundTag":"bech32-invalid-character","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}