FiloSottile/age · error
failed to set up HPKE sender: %v
Error message
failed to set up HPKE sender: %v
What it means
WrapWithLabels creates an HPKE sender against the recipient's public key using HKDF-SHA256, ChaCha20Poly1305, and a KEM-specific info label. NewSender validates the KEM/AEAD/id combination; failure here means the internal recipient key or the HPKE parameter set was rejected.
Source
Thrown at tag/tag.go:144
return tag[:4], nil
}
// WrapWithLabels implements [age.RecipientWithLabels], returning a single
// "postquantum" label if r is a hybrid P-256 + ML-KEM-768 recipient. This
// ensures a hybrid Recipient can't be mixed with other recipients that would
// defeat its post-quantum security.
//
// To unsafely bypass this restriction, wrap Recipient in an [age.Recipient]
// type that doesn't expose WrapWithLabels.
func (r *Recipient) WrapWithLabels(fileKey []byte) ([]*age.Stanza, []string, error) {
label, arg := "age-encryption.org/p256tag", "p256tag"
if r.Hybrid() {
label, arg = "age-encryption.org/mlkem768p256tag", "mlkem768p256tag"
}
enc, s, err := hpke.NewSender(r.pk, hpke.HKDFSHA256(), hpke.ChaCha20Poly1305(), []byte(label))
if err != nil {
return nil, nil, fmt.Errorf("failed to set up HPKE sender: %v", err)
}
ct, err := s.Seal(nil, fileKey)
if err != nil {
return nil, nil, fmt.Errorf("failed to encrypt file key: %v", err)
}
tag, err := r.Tag(enc)
if err != nil {
return nil, nil, fmt.Errorf("failed to compute tag: %v", err)
}
l := &age.Stanza{
Type: arg,
Args: []string{
format.EncodeToString(tag[:4]),
format.EncodeToString(enc),
},
Body: ct,View on GitHub (pinned to b74dce4cdb)
Solutions
- Always obtain the Recipient from tag.ParseRecipient, NewClassicRecipient, or NewHybridRecipient — never instantiate tag.Recipient directly.
- Check the wrapped %v detail for the underlying HPKE error and confirm your filippo.io/hpke version is current (go get -u filippo.io/hpke).
- Verify the key was not truncated or mutated between construction and use.
- If serializing recipients, serialize the raw bytes and re-parse with ParseRecipient rather than persisting structs.
Example fix
// before
r := &tag.Recipient{} // zero value, nil pk
stanzas, err := r.Wrap(fileKey)
// after
r, err := tag.NewHybridRecipient(rawPubKey)
if err != nil { return err }
stanzas, err := r.Wrap(fileKey) Defensive patterns
Strategy: validation
Validate before calling
r, err := tag.NewHybridRecipient(pubKey) // or NewClassicRecipient / ParseRecipient
if err != nil {
return err // fail fast before any Wrap call
} Type guard
func constructedViaAPI(r *tag.Recipient) bool { return r != nil } // zero-value Recipient{} has nil pk and will fail Wrap Try / catch
stanzas, _, err := r.WrapWithLabels(fileKey)
if err != nil {
return fmt.Errorf("wrap failed: %w", err)
} Prevention
- Never construct tag.Recipient directly; always use NewClassicRecipient, NewHybridRecipient, or ParseRecipient.
- Keep filippo.io/hpke up to date.
- Log the wrapped %v detail for diagnosis.
When it happens
Trigger: Calling Wrap/WrapWithLabels on a Recipient whose internal hpke.PublicKey is invalid or whose KEM conflicts with the chosen suites — practically only reachable if the Recipient was constructed with a zero value (&tag.Recipient{} with nil pk) or corrupted via unsafe code, since exported constructors validate keys.
Common situations: Constructing Recipient via reflection/unsafe or a zero-value struct instead of NewClassicRecipient/NewHybridRecipient; deserializing a Recipient from an external format; a mismatched hpke library version where KEM IDs changed.
Related errors
- invalid tagpq recipient public key: %v
- invalid ciphertext size
- failed to create hybrid public key: %v
- failed to encrypt file key: %v
- ArmoredWriter already closed
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/7084c5195867d836.
Report an issue: GitHub.