{"record":{"id":"f701cab5bbc8cd6b","repo":"FiloSottile/age","slug":"mixed-case-hrp-q","errorCode":null,"errorMessage":"mixed case HRP: %q","messagePattern":"mixed case HRP: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bech32/bech32.go","lineNumber":123,"sourceCode":"}\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\n\t}\n\treturn strings.ToUpper(ret.String()), nil\n}\n","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/bech32/bech32.go#L105-L141","documentation":"Bech32 strings must be entirely lowercase or entirely uppercase; mixed-case strings are ambiguous because checksum computation depends on case. bech32.Encode rejects an HRP that is neither all-upper nor all-lower before producing output.","triggerScenarios":"Calling bech32.Encode with an HRP like \"Age\" or \"AGE-secret\" — e.g. user-supplied prefixes, constants partially uppercased by string manipulation, or HRPs assembled from mixed-case segments.","commonSituations":"Title-cased display strings mistakenly passed as HRPs; constants edited so only part was uppercased; concatenation of differently-cased prefixes (\"AGE\" + \"-secret-key-\").","solutions":["Normalize the HRP to all-lowercase (or all-uppercase) before encoding: hrp = strings.ToLower(hrp)","Use age's fixed constants (\"age\" / \"AGE-SECRET-KEY-\") which are already uniform-case","If the HRP is user input, enforce case consistency with a validation check before calling Encode"],"exampleFix":"// before\nhrp := \"Age-Secret-Key\"\ns, err := bech32.Encode(hrp, data) // \"mixed case HRP\"\n\n// after\nhrp := strings.ToLower(\"Age-Secret-Key\") // \"age-secret-key\"\ns, err := bech32.Encode(hrp, data)","handlingStrategy":"validation","validationCode":"func uniformCase(s string) bool {\n\treturn strings.ToLower(s) == s || strings.ToUpper(s) == s\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Normalize HRPs with strings.ToLower before any bech32 operation","Never build HRPs by concatenating differently-cased fragments","Ban title-cased display strings from being used as encoding input","Lint/review any code path where string manipulation touches key prefixes"],"tags":["bech32","validation","case-sensitivity","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"}