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

  1. Treat it as an invalid/expired token: return 400 to the client so it re-issues the first-page query without a token
  2. Verify the same 32-byte key is configured on both encrypting and decrypting sides (check for key rotation drift)
  3. Check the token string is complete — no truncation by URL length limits, proxies, or copy/paste
  4. 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

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


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