{"record":{"id":"6fb3837f9d2ecadb","repo":"FiloSottile/age","slug":"separator-1-at-invalid-position-pos-d-len-d","errorCode":null,"errorMessage":"separator '1' at invalid position: pos=%d, len=%d","messagePattern":"separator '1' at invalid position: pos=(.+?), len=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/bech32/bech32.go","lineNumber":149,"sourceCode":"\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)\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))","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/bech32/bech32.go#L131-L167","documentation":"bech32.Decode rejects an input whose last '1' separator is misplaced. In bech32 the separator must appear at position >= 1 (so the human-readable part is non-empty) and leave at least 6 checksum characters plus one data char after it (pos+7 <= len). This guard prevents decoding strings without a separator, with an empty HRP, or with an impossibly short data part.","triggerScenarios":"Calling Decode (directly or via ParseIdentity/ParseRecipient in age) with a string lacking '1', starting with '1', or with fewer than 7 characters after the last '1', e.g. \"abc12\" or \"1qqqq\".","commonSituations":"Pasting a truncated age recipient/xidentity key, copying only part of an address, or passing a raw hex key instead of the bech32-encoded form.","solutions":["Check the string contains a '1' after a non-empty prefix and has at least 7 characters remaining after the last '1'","Re-copy the full age key/recipient string; truncation is the usual cause","Ensure you are not passing a hex or raw X25519 key where a bech32 AGE- prefixed string is required"],"exampleFix":"// before\ncallersBech32.Decode(\"QQQQ\")\n// after\ncallersBech32.Decode(\"AGE1QQQQ\")","handlingStrategy":"validation","validationCode":"func validBech32Shape(s string) bool {\n    pos := strings.LastIndex(s, \"1\")\n    return pos >= 1 && pos+7 <= len(s)\n}\nif !validBech32Shape(input) { return errors.New(\"not a valid bech32 string\") }","typeGuard":"func looksLikeBech32(s string) bool {\n    i := strings.LastIndex(s, \"1\")\n    return i >= 1 && i+7 <= len(s)\n}","tryCatchPattern":null,"preventionTips":["Store age keys as whole strings; never slice or display them truncated","Use clipboard/programmatic copy instead of manual transcription","Validate shape before passing keys into library calls"],"tags":["bech32","decoding","age"],"backgroundTag":"bech32-separator-invalid","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}