{"record":{"id":"c40a447fbf855e60","repo":"FiloSottile/age","slug":"invalid-hrp-character-hrp-d-d","errorCode":null,"errorMessage":"invalid HRP character: hrp[%d]=%d","messagePattern":"invalid HRP character: hrp\\[(.+?)\\]=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bech32/bech32.go","lineNumber":119,"sourceCode":"\t} else if byte(acc<<(tobits-bits))&maxv != 0 {\n\t\treturn nil, fmt.Errorf(\"non-zero padding\")\n\t}\n\treturn ret, nil\n}\n\n// Encode encodes the HRP and a bytes slice to Bech32. If the HRP is uppercase,\n// the output will be uppercase.\nfunc Encode(hrp string, data []byte) (string, error) {\n\tvalues, err := convertBits(data, 8, 5, true)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(hrp) < 1 {\n\t\treturn \"\", fmt.Errorf(\"invalid HRP: %q\", hrp)\n\t}\n\tfor p, c := range hrp {\n\t\tif c < 33 || c > 126 {\n\t\t\treturn \"\", fmt.Errorf(\"invalid HRP character: hrp[%d]=%d\", p, c)\n\t\t}\n\t}\n\tif strings.ToUpper(hrp) != hrp && strings.ToLower(hrp) != hrp {\n\t\treturn \"\", fmt.Errorf(\"mixed case HRP: %q\", hrp)\n\t}\n\tlower := strings.ToLower(hrp) == hrp\n\thrp = strings.ToLower(hrp)\n\tvar ret strings.Builder\n\tret.WriteString(hrp)\n\tret.WriteString(\"1\")\n\tfor _, p := range values {\n\t\tret.WriteByte(charset[p])\n\t}\n\tfor _, p := range createChecksum(hrp, values) {\n\t\tret.WriteByte(charset[p])\n\t}\n\tif lower {\n\t\treturn ret.String(), nil","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/bech32/bech32.go#L101-L137","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nhrp := string(prefixBytes) // may contain \"\\n\" or non-ASCII\ns, err := bech32.Encode(hrp, data) // \"invalid HRP character\"\n\n// after\nhrp := strings.TrimSpace(string(prefixBytes))\nfor _, c := range hrp {\n    if c < 33 || c > 126 {\n        return fmt.Errorf(\"HRP contains invalid character %q\", c)\n    }\n}\ns, err := bech32.Encode(hrp, data)","handlingStrategy":"validation","validationCode":"func validHRPChars(hrp string) bool {\n\tfor _, c := range hrp {\n\t\tif c < 33 || c > 126 { return false }\n\t}\n\treturn true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["bech32","validation","ascii","hrp","internal"],"backgroundTag":"invalid-bech32-hrp","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}