FiloSottile/age · error
invalid identity encoding: %v
Error message
invalid identity encoding: %v
What it means
ParseIdentity decodes a plugin identity string, which must be a bech32 string whose HRP starts with AGE-PLUGIN- and ends with '-'. This error means the input isn't valid bech32 at all, and it wraps the underlying bech32 decoder error for context.
Source
Thrown at plugin/encode.go:32
"filippo.io/hpke"
)
// EncodeIdentity encodes a plugin identity string for a plugin with the given
// name. If the name is invalid, it returns an empty string.
func EncodeIdentity(name string, data []byte) string {
if !validPluginName(name) {
return ""
}
s, _ := bech32.Encode("AGE-PLUGIN-"+strings.ToUpper(name)+"-", data)
return s
}
// ParseIdentity decodes a plugin identity string. It returns the plugin name
// in lowercase and the encoded data.
func ParseIdentity(s string) (name string, data []byte, err error) {
hrp, data, err := bech32.Decode(s)
if err != nil {
return "", nil, fmt.Errorf("invalid identity encoding: %v", err)
}
if !strings.HasPrefix(hrp, "AGE-PLUGIN-") || !strings.HasSuffix(hrp, "-") {
return "", nil, fmt.Errorf("not a plugin identity: %v", err)
}
name = strings.TrimSuffix(strings.TrimPrefix(hrp, "AGE-PLUGIN-"), "-")
name = strings.ToLower(name)
if !validPluginName(name) {
return "", nil, fmt.Errorf("invalid plugin name: %q", name)
}
return name, data, nil
}
// EncodeRecipient encodes a plugin recipient string for a plugin with the given
// name. If the name is invalid, it returns an empty string.
func EncodeRecipient(name string, data []byte) string {
if !validPluginName(name) {
return ""
}View on GitHub (pinned to b74dce4cdb)
Solutions
- Check the identity file contains a full AGE-PLUGIN-XXXX-1... bech32 string with no truncation or stray whitespace (strings.TrimSpace before parsing)
- Confirm you're using an identity string, not an age1... recipient — they are not interchangeable
- Regenerate or re-export the identity from the plugin tool; the string is likely corrupted
- Read the wrapped %v bech32 error — it pinpoints checksum vs. charset vs. length problems
Example fix
// before
name, data, err := plugin.ParseIdentity(strings.TrimSpace(maybeWithNewline + ""))
// after
s := strings.TrimSpace(rawIdentity)
if s == "" || !strings.HasPrefix(strings.ToUpper(s), "AGE-PLUGIN-") {
return fmt.Errorf("not a plugin identity file")
}
name, data, err := plugin.ParseIdentity(s) Defensive patterns
Strategy: validation
Validate before calling
func looksLikePluginIdentity(s string) bool {
s = strings.TrimSpace(s)
up := strings.ToUpper(s)
return strings.HasPrefix(up, "AGE-PLUGIN-") && strings.HasSuffix(up, "-1") && len(s) > len("AGE-PLUGIN--1")
}
// call before ParseIdentity Type guard
func isPluginIdentity(s string) bool {
_, _, err := plugin.ParseIdentity(strings.TrimSpace(s))
return err == nil
} Try / catch
name, data, err := plugin.ParseIdentity(s)
if err != nil {
return fmt.Errorf("identity file is not a valid plugin identity (bech32 decode failed): %w", err)
} Prevention
- Trim whitespace/newlines from identity strings before parsing
- Distinguish identity (AGE-PLUGIN-...) from recipient (age1...) files
- Validate identity strings at config-load time, not at decrypt time
- Regenerate identities that fail bech32 decoding rather than patching them by hand
When it happens
Trigger: Passing a string that is not bech32 (truncated, wrong checksum, invalid characters, lowercase/uppercase mixing beyond HRP rules, or a completely different format like a raw file path) to ParseIdentity, directly or via NewIdentity/IdentityV1 when parsing AGE-PLUGIN-... identity files.
Common situations: Identity file contains a recipient string instead of an identity, or vice versa; file was truncated by copy/paste or a bad transfer; user pasted a key from a different tool (SSH key, age1... recipient); trailing newline/whitespace not trimmed before parsing.
Related errors
- mixed case
- malformed confirm stanza: invalid YES option encoding
- malformed confirm stanza: invalid NO option encoding
- ArmoredWriter already closed
- trailing data after armored file
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/01c0d61dc3d1e6ec.
Report an issue: GitHub.