nextauthjs/next-auth · error
no matching decryption secret
Error message
no matching decryption secret
What it means
decode() in the Auth.js core tries each configured encryption secret to decrypt a JWE. It derives a thumbprint per secret and compares kid; if no secret matches, it throws 'no matching decryption secret'. This happens when the token was encrypted with a different secret than the ones currently configured.
Source
Thrown at packages/core/src/jwt.ts:100
const { payload } = await jwtDecrypt(
token,
async ({ kid, enc }) => {
for (const secret of secrets) {
const encryptionSecret = await getDerivedEncryptionKey(
enc,
secret,
salt
)
if (kid === undefined) return encryptionSecret
const thumbprint = await calculateJwkThumbprint(
{ kty: "oct", k: base64url.encode(encryptionSecret) },
`sha${encryptionSecret.byteLength << 3}` as Digest
)
if (kid === thumbprint) return encryptionSecret
}
throw new Error("no matching decryption secret")
},
{
clockTolerance: 15,
keyManagementAlgorithms: [alg],
contentEncryptionAlgorithms: [enc, "A256GCM"],
}
)
return payload as Payload
}
type GetTokenParamsBase = {
secret?: JWTDecodeParams["secret"]
salt?: JWTDecodeParams["salt"]
}
export interface GetTokenParams<R extends boolean = false>
extends GetTokenParamsBase {
/** The request containing the JWT either in the cookies or in the `Authorization` header. */View on GitHub (pinned to a1a16a5a77)
Solutions
- Set the same AUTH_SECRET (or `secret` option) that was used to encrypt the token
- If rotating, include the old secret(s) in the `secret` array so existing tokens still decrypt — new tokens use the first
- Confirm the token's kid/alg/enc match the adapter configuration (default dir/dir + A256GCM)
- Re-issue the token/session if the original secret is unrecoverable (users must sign in again)
Example fix
// before
jwtDecode({ token, secret: process.env.NEW_SECRET })
// after
jwtDecode({
token,
secret: [process.env.NEW_SECRET, process.env.OLD_SECRET], // old secret keeps existing tokens working
}) Defensive patterns
Strategy: fallback
Validate before calling
if (!process.env.AUTH_SECRET) throw new Error('AUTH_SECRET missing — tokens cannot be decrypted') Try / catch
try {
return await decode({ token, secret, ...params })
} catch (e) {
if (e.message === 'no matching decryption secret') {
// secret mismatch/rotation — force re-authentication
return null
}
throw e
} Prevention
- Keep AUTH_SECRET identical across environments and stable across deploys
- On rotation, supply old secrets alongside the new one in the `secret` array
- Never share tokens across services with different secrets
- Treat this error as 'session invalid' and require sign-in
When it happens
Trigger: AUTH_SECRET/NEXTAUTH_SECRET changed or rotated after tokens were issued; multiple secrets configured but none matches the token's kid; token encrypted with a different alg/enc than allowed (keyManagementAlgorithms/contentEncryptionAlgorithms); decoding a token from another environment (staging vs prod).
Common situations: Deployments where the secret env var differs between build and runtime; secret rotation invalidating active sessions; sharing tokens across services with different secrets; debugging getToken() on tokens minted before a secret change.
Related errors
- Unsupported JWT Content Encryption Algorithm
- Invalid JWT
- Couldn't create session
- [createSession] Failed to fetch created session
- [updateSession] Failed to fetch updated session
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/66d8539895c348ca.
Report an issue: GitHub.