{"record":{"id":"941c6e86d15fa26b","repo":"ory/hydra","slug":"cannot-create-aead","errorCode":null,"errorMessage":"cannot create AEAD","messagePattern":"cannot create AEAD","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"oryx/pagination/keysetpagination_v2/page_token.go","lineNumber":185,"sourceCode":"\t\tnow = t.testNow\n\t}\n\tif rawToken.ExpiresAt.Before(now().UTC()) {\n\t\treturn errors.WithStack(ErrPageTokenExpired())\n\t}\n\treturn nil\n}\n\nfunc NewPageToken(cols ...Column) PageToken { return PageToken{cols: cols} }\n\nfunc (t *PageToken) encrypt(key [32]byte) (string, error) {\n\traw, err := json.Marshal(t)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"cannot marshal page token\")\n\t}\n\n\ta, err := aead.New(key)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"cannot create AEAD\")\n\t}\n\n\t// The nonce is prepended to the ciphertext. AEADs that manage the nonce\n\t// internally report a nonce size of zero, so this also covers them.\n\tnonce := make([]byte, a.NonceSize())\n\tif _, err := rand.Read(nonce); err != nil {\n\t\treturn \"\", errors.Wrap(err, \"cannot generate nonce\")\n\t}\n\n\treturn base64.URLEncoding.EncodeToString(a.Seal(nonce, nonce, raw, []byte(pageTokenContext))), nil\n}\n\nfunc (t *PageToken) decrypt(key [32]byte, s string) error {\n\tif s == \"\" {\n\t\treturn errors.WithStack(ErrInvalidPaginationToken())\n\t}\n\n\traw, err := base64.URLEncoding.DecodeString(s)","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/oryx/pagination/keysetpagination_v2/page_token.go#L167-L203","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nkey := [32]byte{} // zero value\nenc, err := Encrypt(key, token)\n// after\nkey := sha256.Sum256([]byte(os.Getenv(\"PAGINATION_SECRET\")))\nenc, err := Encrypt(key, token)","handlingStrategy":"validation","validationCode":"func validateKey(key [32]byte) error {\n    if key == ([32]byte{}) {\n        return errors.New(\"pagination encryption key is unset/zero\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"enc, err := Encrypt(key, token)\nif err != nil && strings.Contains(err.Error(), \"cannot create AEAD\") {\n    return fmt.Errorf(\"check PAGINATION_ENCRYPTION_KEY is exactly 32 bytes: %w\", err)\n}","preventionTips":["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"],"tags":["crypto","aead","encryption-key"],"backgroundTag":"invalid-encryption-key","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}