nextauthjs/next-auth · error

Invalid JWT

Error message

Invalid JWT

What it means

When the session strategy is 'jwt', Auth.js decodes the session token cookie using jose with the configured secret and cookie name as salt. If decoding returns no payload (malformed, tampered, or undecryptable token), it throws 'Invalid JWT', which aborts session retrieval.

Source

Thrown at packages/core/src/lib/actions/session.ts:47

      ...(!isUpdate && {
        "Cache-Control": "private, no-cache, no-store",
        Expires: "0",
        Pragma: "no-cache",
      }),
    },
    cookies,
  }

  const sessionToken = sessionStore.value

  if (!sessionToken) return response

  if (sessionStrategy === "jwt") {
    try {
      const salt = options.cookies.sessionToken.name
      const payload = await jwt.decode({ ...jwt, token: sessionToken, salt })

      if (!payload) throw new Error("Invalid JWT")

      // @ts-expect-error
      const token = await callbacks.jwt({
        token: payload,
        ...(isUpdate && { trigger: "update" }),
        session: newSession,
      })

      const newExpires = fromDate(sessionMaxAge)

      if (token !== null) {
        // By default, only exposes a limited subset of information to the client
        // as needed for presentation purposes (e.g. "you are logged in as...").
        const session = {
          user: { name: token.name, email: token.email, image: token.picture },
          expires: newExpires.toISOString(),
        }
        // @ts-expect-error

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Ensure AUTH_SECRET/NEXTAUTH_SECRET is set to the same stable value across all environments and instances sharing session cookies
  2. Log the user out (delete the session cookie) so a fresh token is issued on next sign-in — old invalid cookies cannot be recovered
  3. If migrating next-auth v4 to v5, expect old JWT cookies to be invalid and force re-authentication, or keep the legacy secret during a transition window
  4. Confirm you are not mixing 'database' and 'jwt' strategies with cookies written by the other mode
  5. Verify no middleware or proxy rewrites/truncates the session cookie (large cookies can be split or dropped)

Example fix

// before
// no secret pinned; each deploy generates a new one
// after
export const { handlers, auth, signIn, signOut } = NextAuth({
  secret: process.env.AUTH_SECRET, // stable across deployments
  session: { strategy: 'jwt' },
});
Defensive patterns

Strategy: try-catch

Validate before calling

function isSessionCookieShaped(v: string | undefined): boolean {
  return !!v && v.split('.').length >= 2 && v.length > 20; // JWE/JWT-like shape
}

Type guard

function hasPayload(p: unknown): p is Record<string, unknown> {
  return typeof p === 'object' && p !== null && 'sub' in (p as object);
}

Try / catch

try {
  const session = await auth();
} catch (e) {
  if (/Invalid JWT/.test(String(e))) {
    // clear the stale session cookie and redirect to sign-in
    await signOut({ redirect: false });
  }
}

Prevention

When it happens

Trigger: The sessionToken cookie exists but its payload cannot be decoded: cookie encrypted/signed with a different AUTH_SECRET, token from another deployment/environment, manually crafted cookie value, or a token corrupted by truncation.

Common situations: Changing or losing NEXTAUTH_SECRET/AUTH_SECRET between deploys while users hold old cookies; running multiple instances with different secrets; switching between next-auth v4 and Auth.js v5 (different token formats); copying session cookies between localhost and production.

Related errors


AI-assisted analysis of nextauthjs/next-auth@a1a16a5a77 (2026-08-28). Data as JSON: /api/errors/bba71756f18cf8bc. Report an issue: GitHub.