nextauthjs/next-auth · error
Invalid state
Error message
Invalid state
What it means
state.decode throws the plain Error "Invalid state" when JWT-decoding the state token with options.jwt.secret and the encoded-state salt returns no payload. It is immediately wrapped into InvalidCheck("State could not be decoded"), so developers normally see that outer message with this as cause.
Source
Thrown at packages/core/src/lib/actions/callback/oauth/checks.ts:200
return { cookie, value }
},
/**
* Returns state if the provider is configured to use state,
* and clears the container cookie afterwards.
* An error is thrown if the state is missing or invalid.
*/
use: useCookie("state", "state"),
/** Decodes the state. If it could not be decoded, it throws an error. */
async decode(state: string, options: InternalOptions) {
try {
options.logger.debug("DECODE_STATE", { state })
const payload = await decode<EncodedState>({
secret: options.jwt.secret,
token: state,
salt: encodedStateSalt,
})
if (payload) return payload
throw new Error("Invalid state")
} catch (error) {
throw new InvalidCheck("State could not be decoded", { cause: error })
}
},
}
export const nonce = {
async create(options: InternalOptions<"oidc">) {
if (!options.provider.checks.includes("nonce")) return
const value = o.generateRandomNonce()
const cookie = await sealCookie("nonce", value, options)
return { cookie, value }
},
/**
* Returns nonce if the provider is configured to use nonce,
* and clears the container cookie afterwards.
* An error is thrown if the nonce is missing or invalid.
* @see https://openid.net/specs/openid-connect-core-1_0.html#NonceNotesView on GitHub (pinned to a1a16a5a77)
Solutions
- Set a single explicit AUTH_SECRET shared across all instances and stable across deploys.
- Have the user restart the sign-in flow to generate fresh state.
- Inspect error.cause for JWT verification details (signature/expiry).
- Don't hand-edit or reuse old callback URLs.
Defensive patterns
Strategy: try-catch
Validate before calling
if (!process.env.AUTH_SECRET) throw new Error("AUTH_SECRET required for state signing") Try / catch
try {
const session = await auth()
} catch (e) {
if (e?.message?.includes("State could not be decoded")) {
// redirect user to start sign-in again
}
} Prevention
- Use a persistent shared AUTH_SECRET
- Treat state as single-use: always restart the flow on failure
- Never modify callback URLs manually
When it happens
Trigger: The state query parameter on the callback cannot be decoded into an EncodedState payload — expired/tampered JWT, wrong AUTH_SECRET, or a state value not produced by this app.
Common situations: Multi-instance deployments with different secrets; state JWT from a previous deploy/secret; manually edited callback URLs; very old in-flight sign-in attempts after a secret rotation.
Related errors
- State could not be decoded
- OAuth Provider returned an error
- ${name} cookie was missing
- Invalid cookie
- ${name} value could not be parsed
AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28).
Data as JSON: /api/errors/eebe515e13af87b1.
Report an issue: GitHub.