ory/hydra · error
cannot open AEAD
Error message
cannot open AEAD
What it means
openAEAD calls a.Open to authenticate and decrypt the page token ciphertext with the associated data (pageTokenContext). If authentication fails — wrong key, corrupted/truncated ciphertext, tampered token, or missing/changed associated data — the AEAD refuses to open and this error is returned.
Source
Thrown at oryx/pagination/keysetpagination_v2/page_token.go:241
}
return errors.WithStack(herodot.ErrInternalServerError().WithReason("unable to unmarshal page token").WithDebug(err.Error()))
}
return nil
}
func openAEAD(key [32]byte, raw []byte) ([]byte, error) {
a, err := aead.New(key)
if err != nil {
return nil, errors.Wrap(err, "cannot create AEAD")
}
if len(raw) < a.NonceSize() {
return nil, errors.New("ciphertext too short")
}
nonce, ciphertext := raw[:a.NonceSize()], raw[a.NonceSize():]
bs, err := a.Open(nil, nonce, ciphertext, []byte(pageTokenContext))
if err != nil {
return nil, errors.Wrap(err, "cannot open AEAD")
}
return bs, nil
}
View on GitHub (pinned to 4174065ffb)
Solutions
- Treat it as an invalid/expired token: return 400 to the client so it re-issues the first-page query without a token
- Verify the same 32-byte key is configured on both encrypting and decrypting sides (check for key rotation drift)
- Check the token string is complete — no truncation by URL length limits, proxies, or copy/paste
- If the error appeared after a deployment/upgrade, roll back the key or library change and re-test
Example fix
// server side
var pt PageToken
if err := Decrypt(key, token, &pt); err != nil {
return nil, httpErr_BAD_REQUEST("invalid page token") // restart pagination
} Defensive patterns
Strategy: try-catch
Validate before calling
func tokenLooksValid(s string) bool {
if s == "" { return false }
raw, err := base64.URLEncoding.DecodeString(s)
return err == nil && len(raw) > 12 // at least nonce-sized
} Type guard
func isDecryptable(key [32]byte, s string, pt *PageToken) bool {
return Decrypt(key, s, pt) == nil
} Try / catch
var pt PageToken
if err := Decrypt(key, token, &pt); err != nil {
if strings.Contains(err.Error(), "cannot open AEAD") {
// treat as invalid/expired cursor: restart pagination from page 1
return listFirstPage(ctx, req)
}
return err
} Prevention
- Return HTTP 400 (not 500) for AEAD-open failures so clients restart pagination
- Never accept user tokens across key rotations without a fallback key
- Avoid truncating tokens: raise URL length limits and don't log/copy partial tokens
- Keep pageTokenContext stable across library versions within a deployment
When it happens
Trigger: Decrypting a page token whose ciphertext does not verify: token modified in transit, decrypted with a different key than it was sealed with, base64 payload truncated, or pageTokenContext changed between encrypt and decrypt (version mismatch).
Common situations: Client holds a page token across a deployment that rotated the encryption key; token copied with truncation from a URL; malicious tampering attempt; upgrading the library changed the AEAD context/algorithm.
Related errors
- ErrMismatchedHashAndPassword
- key must be exactly 32 long bytes, got %d bytes
- cannot create XChaCha20-Poly1305 AEAD
- cannot create AEAD
- cookiex: at least one secret is required
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/4297f4545258ee94.
Report an issue: GitHub.