{"record":{"id":"fdec72dcd97220d4","repo":"FiloSottile/age","slug":"mixed-case","errorCode":null,"errorMessage":"mixed case","messagePattern":"mixed case","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bech32/bech32.go","lineNumber":145,"sourceCode":"\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\n// Decode decodes a Bech32 string. If the string is uppercase, the HRP will be uppercase.\nfunc Decode(s string) (hrp string, data []byte, err error) {\n\tif strings.ToLower(s) != s && strings.ToUpper(s) != s {\n\t\treturn \"\", nil, fmt.Errorf(\"mixed case\")\n\t}\n\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)","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/bech32/bech32.go#L127-L163","documentation":"bech32.Decode requires the input string to be uniformly cased: if the string is neither all-lowercase nor all-uppercase, decoding is ambiguous because the bech32 checksum is case-dependent. Decode rejects mixed-case strings with this error. age's ParseRecipient/ParseIdentity rely on this validation.","triggerScenarios":"Calling bech32.Decode (or agessh.ParseRecipient/ParseIdentity) with a string like \"Age1abcDEF\" — typically from manual typing, copy/paste that preserved inconsistent case, or display code that title-cased the key for presentation and it got fed back as input.","commonSituations":"Users re-typing an age key with mixed case; documents/UI that auto-capitalize (phone autocorrect, title-case formatting); logs showing prettified keys pasted back into tools; concatenating case-normalized and raw fragments of a key.","solutions":["Normalize the input before decoding: if the string contains letters, convert entirely with strings.ToLower(s) (or ToUpper) and pass that to Decode","Re-copy the key from its original source (age-keygen output, config file) without retyping, avoiding auto-capitalizing editors","Disable auto-capitalization/formatting in UIs or shells where keys are entered, and never store title-cased display forms as the canonical value"],"exampleFix":"// before\nid, err := agessh.ParseIdentity(userInput) // \"Age1...\" fails: \"mixed case\"\n\n// after\ncanonical := strings.ToLower(strings.TrimSpace(userInput))\nid, err := agessh.ParseIdentity(canonical)","handlingStrategy":"validation","validationCode":"func isUniformCaseBech32(s string) bool {\n\tif strings.ToLower(s) != s && strings.ToUpper(s) != s {\n\t\treturn false // would trigger \"mixed case\"\n\t}\n\treturn strings.Contains(s, \"1\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Normalize input with strings.TrimSpace + strings.ToLower before parsing any bech32 string","Never retype keys manually; always copy from the source file verbatim","Disable auto-capitalization in UIs, terminals, and note apps where keys are handled","Store canonical lowercase forms; apply case changes only at display time"],"tags":["bech32","decoding","case-sensitivity","input-validation","age"],"backgroundTag":"bech32-mixed-case","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}