ory/hydra · error
cannot create AEAD
Error message
cannot create AEAD
What it means
PageToken.encrypt creates an AEAD (authenticated encryption with associated data) cipher from a 32-byte key via aead.New. This error indicates the key material passed to Encrypt was rejected — the ChaCha20-Poly1305/XChaCha20 AEAD constructor fails only when the key is the wrong size or otherwise invalid.
Source
Thrown at oryx/pagination/keysetpagination_v2/page_token.go:185
now = t.testNow
}
if rawToken.ExpiresAt.Before(now().UTC()) {
return errors.WithStack(ErrPageTokenExpired())
}
return nil
}
func NewPageToken(cols ...Column) PageToken { return PageToken{cols: cols} }
func (t *PageToken) encrypt(key [32]byte) (string, error) {
raw, err := json.Marshal(t)
if err != nil {
return "", errors.Wrap(err, "cannot marshal page token")
}
a, err := aead.New(key)
if err != nil {
return "", errors.Wrap(err, "cannot create AEAD")
}
// The nonce is prepended to the ciphertext. AEADs that manage the nonce
// internally report a nonce size of zero, so this also covers them.
nonce := make([]byte, a.NonceSize())
if _, err := rand.Read(nonce); err != nil {
return "", errors.Wrap(err, "cannot generate nonce")
}
return base64.URLEncoding.EncodeToString(a.Seal(nonce, nonce, raw, []byte(pageTokenContext))), nil
}
func (t *PageToken) decrypt(key [32]byte, s string) error {
if s == "" {
return errors.WithStack(ErrInvalidPaginationToken())
}
raw, err := base64.URLEncoding.DecodeString(s)View on GitHub (pinned to 4174065ffb)
Solutions
- Verify the encryption key is exactly 32 random bytes: head -c 32 /dev/urandom | base64
- Check the config/env providing the pagination encryption key is set and not empty
- Re-derive the key with a KDF (sha256 of secret) to guarantee 32 bytes before calling Encrypt
- Pin/upgrade the golang.org/x/crypto aead package if a version bug is suspected
Example fix
// before
key := [32]byte{} // zero value
enc, err := Encrypt(key, token)
// after
key := sha256.Sum256([]byte(os.Getenv("PAGINATION_SECRET")))
enc, err := Encrypt(key, token) Defensive patterns
Strategy: validation
Validate before calling
func validateKey(key [32]byte) error {
if key == ([32]byte{}) {
return errors.New("pagination encryption key is unset/zero")
}
return nil
} Try / catch
enc, err := Encrypt(key, token)
if err != nil && strings.Contains(err.Error(), "cannot create AEAD") {
return fmt.Errorf("check PAGINATION_ENCRYPTION_KEY is exactly 32 bytes: %w", err)
} Prevention
- Generate keys with head -c 32 /dev/urandom | base64
- Derive fixed-size keys via sha256.Sum256([]byte(secret))
- Fail fast at startup if the configured key is empty or wrong length
- Never pass a zero-value [32]byte
When it happens
Trigger: Calling Encrypt with a key that is not exactly 32 bytes at the point of aead.New — e.g. a zero-value key from an unset config, or a key derived incorrectly (short/empty secret).
Common situations: Pagination encryption key env var missing so a zeroed [32]byte is used with a misconfigured aead implementation, or application passed a shorter key cast into [32]byte incorrectly upstream.
Related errors
- key must be exactly 32 long bytes, got %d bytes
- cannot create XChaCha20-Poly1305 AEAD
- cannot open AEAD
- cookiex: at least one secret is required
- ErrUnknownHashAlgorithm
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/941c6e86d15fa26b.
Report an issue: GitHub.