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

  1. Verify the encryption key is exactly 32 random bytes: head -c 32 /dev/urandom | base64
  2. Check the config/env providing the pagination encryption key is set and not empty
  3. Re-derive the key with a KDF (sha256 of secret) to guarantee 32 bytes before calling Encrypt
  4. 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

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


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/941c6e86d15fa26b. Report an issue: GitHub.