{"record":{"id":"b1cfbdfb4458a191","repo":"FiloSottile/age","slug":"invalid-ssh-ed25519-recipient-block","errorCode":null,"errorMessage":"invalid ssh-ed25519 recipient block","messagePattern":"invalid ssh-ed25519 recipient block","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agessh/agessh.go","lineNumber":315,"sourceCode":"}\n\nfunc (i *Ed25519Identity) Recipient() *Ed25519Recipient {\n\treturn &Ed25519Recipient{\n\t\tsshKey:         i.sshKey,\n\t\ttheirPublicKey: i.ourPublicKey,\n\t}\n}\n\nfunc (i *Ed25519Identity) Unwrap(stanzas []*age.Stanza) ([]byte, error) {\n\treturn multiUnwrap(i.unwrap, stanzas)\n}\n\nfunc (i *Ed25519Identity) unwrap(block *age.Stanza) ([]byte, error) {\n\tif block.Type != \"ssh-ed25519\" {\n\t\treturn nil, age.ErrIncorrectIdentity\n\t}\n\tif len(block.Args) != 2 {\n\t\treturn nil, errors.New(\"invalid ssh-ed25519 recipient block\")\n\t}\n\tpublicKey, err := format.DecodeString(block.Args[1])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse ssh-ed25519 recipient: %v\", err)\n\t}\n\tif len(publicKey) != curve25519.PointSize {\n\t\treturn nil, errors.New(\"invalid ssh-ed25519 recipient block\")\n\t}\n\n\tif block.Args[0] != sshFingerprint(i.sshKey) {\n\t\treturn nil, age.ErrIncorrectIdentity\n\t}\n\n\tsharedSecret, err := curve25519.X25519(i.secretKey, publicKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid X25519 recipient: %v\", err)\n\t}\n","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/agessh/agessh.go#L297-L333","documentation":"Ed25519Identity.unwrap accepts only \"ssh-ed25519\" stanzas carrying exactly two arguments: the ephemeral share and the base64-encoded (RawStdEncoding) X25519 public key of the recipient. This error means the stanza is structurally malformed — wrong argument count — as opposed to belonging to a different identity (which returns age.ErrIncorrectIdentity). It indicates a corrupted or non-conforming age header.","triggerScenarios":"Calling Unwrap with a stanza where Type == \"ssh-ed25519\" but len(Args) != 2 — edited, truncated, or non-standard-conformant headers; buggy third-party writers.","commonSituations":"Hand-edited age files; malformed-header fuzzing/security testing; files produced by non-compliant age implementations or damaged in transit.","solutions":["Re-acquire or re-encrypt the file; the stanza cannot be repaired reliably.","Inspect the file's stanza args (count and decodability) before unwrapping.","If you generate stanzas in your own tooling, emit exactly two args: ephemeral share and format.EncodeString(publicKey).","Treat it as a parse failure, not an identity mismatch — do not retry with other identities expecting success."],"exampleFix":"// before (producer)\nst := &age.Stanza{Type: \"ssh-ed25519\", Args: []string{ephemeral}} // missing key arg\n// after\nst := &age.Stanza{Type: \"ssh-ed25519\", Args: []string{ephemeral, format.EncodeString(publicKey)}}","handlingStrategy":"try-catch","validationCode":"if block.Type == \"ssh-ed25519\" && len(block.Args) != 2 {\n    return fmt.Errorf(\"malformed ssh-ed25519 stanza: %d args\", len(block.Args))\n}","typeGuard":"func isWellFormedEd25519Block(b *age.Stanza) bool {\n    return b != nil && b.Type == \"ssh-ed25519\" && len(b.Args) == 2\n}","tryCatchPattern":"fileKey, err := identity.Unwrap(stanza)\nif err != nil {\n    if !errors.Is(err, age.ErrIncorrectIdentity) {\n        return fmt.Errorf(\"corrupt age header: %w\", err) // structural, not identity mismatch\n    }\n    return err // safe to try next identity\n}","preventionTips":["Treat age file headers as immutable; never edit stanza args manually.","Use binary-safe transfer to avoid header corruption.","When emitting stanzas yourself, always include both ephemeral share and encoded public key args.","Classify errors: only age.ErrIncorrectIdentity should trigger trying the next identity."],"tags":["age","stanza","malformed-input","ssh-ed25519"],"backgroundTag":"malformed-age-stanza","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}