Free tool · Auth.js v5 · Next.js
Paste the Auth.js error.
Get the fix.
15 errors that break sign-in on Next.js, mostly right after you deploy. Each one comes with what it means, the fix and a prompt you can hand to Cursor or Claude Code.
› 15 errors indexed. Matching runs locally as you type.
- 15 errors
- 5 only show up in production
- checked against next-auth 5.0.0-beta.32 · @auth/core 0.41.3 · Next.js 16.4.3
- updated
01Works locally, breaks in prod
Your laptop is forgiving. Production isn't.
In development Auth.js trusts any host, reads secrets from .env.local and talks to the GitHub app you made for localhost. After you deploy, none of that holds. These are the 5 errors that only appear there:
- 01MissingSecretAUTH_SECRET is in .env.local but not in your host's environment variablesFix: set AUTH_SECRET on the host, redeploy →
- 02UntrustedHostRunning next start, Docker, Railway, Fly, Render or a VPS behind a proxyFix: trustHost: true or AUTH_TRUST_HOST=true →
- 03GitHub: redirect_uri mismatchSigning in on production with the localhost OAuth app's client IDFix: a second GitHub OAuth app for prod →
- 04Redirected to localhost after deployNEXTAUTH_URL=http://localhost:3000 copied from a v4 tutorial into the host's env varsFix: delete AUTH_URL / NEXTAUTH_URL in prod →
- 05AdapterError / table does not existMigrations never ran against the production databaseFix: npx prisma migrate deploy on Postgres →
- pre-deploy check
AUTH_SECRETset on the hosttrustHost: true(if not on Vercel)- Production GitHub OAuth app
- No localhost
AUTH_URL - Migrations run on prod DB
02Every error, explained
The full list.
error=Configuration
/api/auth/error?error=Configuration
This is a cover, not the real error. Auth.js only sends a few error types to the browser (AccessDenied, Verification, OAuthAccountNotLinked and a handful more). Everything else, like a missing secret or a database failure, shows up as Configuration so it can't leak details.
usually because
- The real error is in your server logs, on a line starting with [auth][error]
- Most often MissingSecret, UntrustedHost or an AdapterError
fix
- 1.Open the logs where the app runs (Vercel → Deployments → Logs, or your terminal)
- 2.Find the [auth][error] line and paste it into the box above
- 3.Add
debug: trueto your NextAuth config temporarily if the line isn't there
Sign-in redirects to /api/auth/error?error=Configuration. Don't guess. Tell me where my server logs are for this host, then read the [auth][error] line and fix only that root cause.
MissingSecret
[auth][error] MissingSecret: Please define a `secret`. Read more at https://errors.authjs.dev#missingsecret
Auth.js has no secret to encrypt cookies and tokens with. It reads AUTH_SECRET (or AUTH_SECRET_1, _2, _3 for rotation). next-auth v5 still falls back to the old NEXTAUTH_SECRET.
usually because
AUTH_SECRETis in.env.localbut not in your host's environment variables- The variable was added after the last deploy, so the running build doesn't see it
- A typo like
AUTH_SECRETSor NEXT_PUBLIC_AUTH_SECRET
fix
- 1.Generate one:
npx auth secret(oropenssl rand -base64 33) - 2.Add
AUTH_SECRETto the production environment on your host - 3.Redeploy so the new value is picked up
- 4.Never prefix it with NEXT_PUBLIC_: that ships it to the browser
# at least 32 random characters
AUTH_SECRET="paste-the-output-of-npx-auth-secret"Auth.js throws MissingSecret in production. Check which env vars my auth.ts reads, confirm AUTH_SECRET is set for the production environment on my host (not only in .env.local), and make sure it is never exposed with a NEXT_PUBLIC_ prefix.
UntrustedHost
[auth][error] UntrustedHost: Host must be trusted. URL was: https://your-app.com/api/auth/session. Read more at https://errors.authjs.dev#untrustedhost
Auth.js builds callback URLs from the request's Host header and refuses to trust it by default in production. In development (NODE_ENV not production) it trusts the host automatically, which is why this only appears after you deploy.
usually because
- Running
next start, Docker, Railway, Fly, Render or a VPS behind a proxy - Vercel and Cloudflare Pages are detected automatically (VERCEL / CF_PAGES env vars), most other hosts are not
fix
- 1.Set
trustHost: truein your NextAuth config, or - 2.Set
AUTH_TRUST_HOST=true in the production environment - 3.Setting
AUTH_URLalso counts as trusting the host
export const { handlers, auth, signIn, signOut } = NextAuth({
trustHost: true, // needed for next start, Docker and most non-Vercel hosts
providers: [GitHub],
});Auth.js logs UntrustedHost after deploying with next start. Add trustHost: true to the NextAuth config in auth.ts (or tell me to set AUTH_TRUST_HOST=true on the host) and explain why it worked locally.
GitHub: redirect_uri mismatch
error=redirect_uri_mismatch · The redirect_uri is not associated with this application.
The callback URL Auth.js sent to GitHub doesn't match the one registered on your GitHub OAuth app. A GitHub OAuth app has one callback URL, so the app you made for localhost can't also serve production.
usually because
- Signing in on production with the localhost OAuth app's client ID
- http vs https, www vs no www, or a different port
- A preview deployment URL that changes on every deploy
fix
- 1.Create a second GitHub OAuth app for production (github.com/settings/developers)
- 2.Set its callback URL to https://your-domain.com/api/auth/callback/github
- 3.Put that app's ID and secret in production as
AUTH_GITHUB_IDandAUTH_GITHUB_SECRET - 4.For previews, Auth.js supports a stable redirect proxy via
AUTH_REDIRECT_PROXY_URL
Homepage URL: https://your-domain.com
Callback URL: https://your-domain.com/api/auth/callback/githubGitHub sign-in fails in production with redirect_uri_mismatch. List the exact callback URL my app sends (base path /api/auth, provider github) for my production domain, and the env vars I need for a separate production GitHub OAuth app.
Redirected to localhost after deploy
https://your-app.com → sign in → http://localhost:3000/api/auth/callback/github
If AUTH_URL or NEXTAUTH_URL is set, next-auth v5 rewrites the request's origin to it. A localhost value copied into production sends every user back to localhost.
usually because
NEXTAUTH_URL=http://localhost:3000 copied from a v4 tutorial into the host's env varsAUTH_URLleft over from local testing
fix
- 1.Delete
AUTH_URLandNEXTAUTH_URLfrom production. v5 infers the host from the request - 2.Only set
AUTH_URLwhen Auth.js lives under a different base path - 3.Redeploy
After deploying, sign-in sends users to localhost. Find every place AUTH_URL or NEXTAUTH_URL is set or read, and tell me which ones to remove for production on Auth.js v5.
OAuthAccountNotLinked
/login?error=OAuthAccountNotLinked
Someone already has an account with this email, created with a different sign-in method (say, a magic link), and now tries GitHub. For security, Auth.js won't attach the new provider to an existing user who isn't signed in.
usually because
- Same email used with email magic link first, GitHub second (or the reverse)
- Test users left in your dev database
fix
- 1.Show a friendly message on your sign-in page: "Sign in the way you did the first time"
- 2.Let signed-in users link extra providers from their account page
- 3.
allowDangerousEmailAccountLinking: trueon a provider turns on auto-linking. Only use it if you trust that provider to verify emails
const messages: Record<string, string> = {
OAuthAccountNotLinked: "That email already has an account. Sign in the way you did the first time.",
Verification: "That sign-in link expired or was already used. Request a new one.",
};Users who signed up with a magic link get OAuthAccountNotLinked when they try GitHub. Show a clear message for ?error=OAuthAccountNotLinked on my sign-in page. Don't enable allowDangerousEmailAccountLinking unless I confirm.
error=Verification
/api/auth/error?error=Verification
The magic-link token wasn't found or has expired. Each link works once, and the email provider's default lifetime is 24 hours.
usually because
- The link was clicked twice, or a newer link replaced it
- The link is older than 24 hours
- The verification token was written to a different database than the one the app reads now
fix
- 1.Ask the user to request a new link, and say so on the error page
- 2.Make sure dev and prod don't share or swap
DATABASE_URL - 3.Raise maxAge on the email provider only if you have a reason to
Magic-link sign-in lands on error=Verification. Check my email provider config, the VerificationToken table and DATABASE_URL per environment, and add a 'request a new link' message for this error.
MissingAdapter
[auth][error] MissingAdapter: Email login requires an adapter. Read more at https://errors.authjs.dev#missingadapter
Magic links (Email / Nodemailer / Resend providers) and session: { strategy: "database" } both need a database adapter to store tokens and sessions.
usually because
- Added the Nodemailer or Resend provider without an adapter
- Set
strategy: "database"without an adapter - Imported the adapter but passed it in only one of two config files
fix
- 1.Install
@auth/prisma-adapter(v5 adapters live under @auth/, not@next-auth/) - 2.Pass adapter: PrismaAdapter(db) to NextAuth
- 3.Add the User, Account, Session and VerificationToken models and run a migration
import { PrismaAdapter } from "@auth/prisma-adapter";
import { db } from "@/lib/db";
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(db),
session: { strategy: "database" },
providers,
});Auth.js says MissingAdapter. Wire @auth/prisma-adapter into auth.ts, check my Prisma schema has the User, Account, Session and VerificationToken models from the Auth.js Prisma adapter docs, and give me the migration command.
AdapterError / table does not exist
[auth][error] AdapterError … The table `main.User` does not exist in the current database.
One of the adapter's database calls failed. The cause line under it says why, and with Prisma it's usually a missing table or an unreachable database.
usually because
- Migrations never ran against the production database
DATABASE_URLpoints to SQLite on a serverless host, where the file doesn't persist- Schema models renamed so they no longer match what the adapter expects
fix
- 1.Run
npx prisma migrate deployagainst production (or prisma db push while prototyping) - 2.Use Postgres in production. SQLite files don't survive serverless deploys
- 3.Set
debug: trueonce to see which adapter_ method failed
Auth.js logs AdapterError with a Prisma 'table does not exist' cause in production. Check DATABASE_URL and the Prisma provider for prod, then give me the exact migrate command to run and where to run it.
InvalidCheck (PKCE / state)
[auth][error] InvalidCheck: pkceCodeVerifier value could not be parsed. Read more at https://errors.authjs.dev#invalidcheck
Auth.js sets a short-lived cookie when sign-in starts and checks it when the provider sends the user back. The cookie was missing on the way back.
usually because
- Starting on 127.0.0.1 and coming back to localhost (or www vs no www): different cookie jars
- The callback lands on a different domain than the one sign-in started on
- Cookies blocked in the browser, or a custom cookies config
fix
- 1.Use one exact origin everywhere: the address bar, the OAuth app and any
AUTH_URL - 2.Redirect www to the apex domain (or the reverse) at the host
- 3.Remove custom cookies options unless you need them
GitHub sign-in fails with InvalidCheck (pkceCodeVerifier). Check whether sign-in starts and returns on the same origin (localhost vs 127.0.0.1, www vs apex), and whether my config overrides cookies.
JWTSessionError
[auth][error] JWTSessionError: Read more at https://errors.authjs.dev#jwtsessionerror [auth][cause]: Error: no matching decryption secret
With strategy "jwt", Auth.js couldn't decrypt the session cookie. The cookie was encrypted with a different secret, so it's destroyed and the user is signed out.
usually because
AUTH_SECRETchanged (or differs between preview and production)- An old cookie from another app on localhost:3000
fix
- 1.For one user: clear cookies for the site and sign in again
- 2.To rotate without signing everyone out, set the new secret as
AUTH_SECRETand keep the old one asAUTH_SECRET_1for a while - 3.Use the same secret on every deployment that shares the domain
Auth.js logs JWTSessionError: no matching decryption secret. Check whether AUTH_SECRET differs between environments and set up AUTH_SECRET_1 rotation if I changed it on purpose.
SessionTokenError
[auth][error] SessionTokenError: Read more at https://errors.authjs.dev#sessiontokenerror
With strategy "database", Auth.js couldn't read the session from the database. The adapter is misconfigured or the database can't be reached.
usually because
- Database down or
DATABASE_URLwrong in this environment - Connection limit hit on serverless Postgres without pooling
fix
- 1.Check the cause line and your database dashboard
- 2.Use your provider's pooled connection string on serverless
- 3.Reuse one Prisma client (a global singleton in dev)
Auth.js logs SessionTokenError with database sessions. Check DATABASE_URL for this environment, whether I use a pooled connection on serverless, and that my Prisma client is a singleton.
AccessDenied
/api/auth/error?error=AccessDenied
Your signIn callback returned false (or threw), or the user clicked Cancel on the provider's consent screen.
usually because
- An allow-list in callbacks.signIn that doesn't include this email
- The user declined on GitHub
fix
- 1.Log the inputs in callbacks.signIn to see why it returned false
- 2.Return a redirect path string from signIn instead of false to send blocked users somewhere useful
Sign-in ends at error=AccessDenied. Show me my callbacks.signIn logic, log why it returns false for this user, and suggest returning a friendly redirect instead.
MissingCSRF
[auth][error] MissingCSRF: CSRF token was missing during an action signin. Read more at https://errors.authjs.dev#missingcsrf
A sign-in or sign-out POST arrived without Auth.js's CSRF cookie and token. Often it's a hand-rolled fetch to /api/auth/signin instead of the provided helpers.
usually because
- fetch('/api/auth/signin/…', { method: 'POST' }) written by hand
- A form posting to the Auth.js endpoint from another origin
fix
- 1.Use
signIn()from yourauth.tsin a Server Action, or signIn from next-auth/react in a Client Component - 2.Don't call the /api/auth endpoints directly
import { signIn } from "@/auth";
<form action={async () => { "use server"; await signIn("github", { redirectTo: "/dashboard" }); }}>
<button>Sign in with GitHub</button>
</form>Auth.js logs MissingCSRF. Find any hand-written fetch or form that posts to /api/auth/* and replace it with signIn() from auth.ts in a Server Action.
v4 code in a v5 app (getServerSession, authOptions)
Module '"next-auth"' has no exported member 'NextAuthOptions'. · getServerSession(authOptions)
Your AI agent wrote NextAuth v4 code. A lot of tutorials and training data are from v4, so agents mix the two APIs unless you tell them which version you use.
usually because
- getServerSession(authOptions) and next-auth/next imports
- NextAuthOptions (renamed NextAuthConfig)
- export { default } from "next-auth/middleware"
- @next-auth/prisma-adapter (now
@auth/prisma-adapter)
fix
- 1.Call
auth()from yourauth.tseverywhere on the server - 2.Export auth as proxy in
proxy.ts(Next.js 16 renamedmiddleware.ts) - 3.Add a rule telling your agent the project uses Auth.js v5 and to check package.json before writing auth code
- import { getServerSession } from "next-auth/next"
- const session = await getServerSession(authOptions)
+ import { auth } from "@/auth"
+ const session = await auth()This project uses Auth.js v5 (next-auth@5). Search for getServerSession, authOptions, NextAuthOptions, next-auth/next, next-auth/middleware, getToken and @next-auth/ imports and migrate each one to the v5 auth() API. List every file you change.
03Or skip the debugging
Start from an Auth.js v5 setup that already deploys.
Skip the setup. The VibeBoot SaaS Starter ships Auth.js v5 already wired (GitHub sign-in, optional email magic links, Prisma adapter), plus Stripe and a database on Next.js 16.4.3. $49, one-time.
14-day money-back guarantee · lifetime updates
See everything that's inside →- trustHost: true · already set in
auth.ts, sonext startand Docker don't throw UntrustedHost - AUTH_SECRET · documented in
.env.examplewith thenpx auth secretcommand - Production GitHub app · README and launch checklist walk through the second OAuth app and its callback URL
- Magic links optional · the email provider only turns on when
EMAIL_SERVERis set, so builds don't need SMTP - v5 only · AGENTS.md, CLAUDE.md and .cursor/rules tell your agent to use
auth(), not v4 APIs - Prisma adapter · database sessions with SQLite by default; Postgres by changing the provider and
DATABASE_URL
FAQ
Why does Auth.js work locally but fail after I deploy?
Three defaults change in production. Auth.js trusts the Host header automatically only when NODE_ENV isn't production, so UntrustedHost appears on next start, Docker and most non-Vercel hosts. AUTH_SECRET is often only in .env.local, which your host never sees. And a GitHub OAuth app has a single callback URL, so the localhost app can't serve your real domain.
What does error=Configuration mean in NextAuth v5?
It's a generic error page. Auth.js only sends a few error types to the browser (such as AccessDenied, Verification and OAuthAccountNotLinked), and shows everything else as Configuration so server details don't leak. The real error is on the [auth][error] line in your server logs.
Do I still need NEXTAUTH_URL and NEXTAUTH_SECRET in v5?
No. v5 uses the AUTH_ prefix and infers the URL from the request. next-auth v5 still reads NEXTAUTH_SECRET as a fallback, but a leftover NEXTAUTH_URL or AUTH_URL pointing at localhost will redirect production users to localhost, so remove it unless you use a custom base path.
Is anything I paste sent to a server?
No. The matcher is plain JavaScript in this page. Nothing you paste is stored or sent anywhere, but it's still good practice to strip secrets and emails from logs before sharing them.
Which Auth.js version is this for?
Auth.js v5 for Next.js (the next-auth@beta package), checked against next-auth 5.0.0-beta.32 · @auth/core 0.41.3 · Next.js 16.4.3 and the Auth.js docs on October 27, 2026. Most entries also apply to other Auth.js frameworks, but their default base path is /auth instead of /api/auth.
Sources: the Auth.js errors reference, deployment guide and v5 migration guide, the next-auth 5.0.0-beta.32 · @auth/core 0.41.3 · Next.js 16.4.3 source, and GitHub's OAuth troubleshooting docs. Checked October 27, 2026. Spotted a mistake? Email [email protected].
More free tools: Cursor Rules Pack · AGENTS.md generator · newsletter
$ fixed it? get the next one first
New fixes for what AI agents break, by email.
When we add a fix or a Cursor rule for a new Next.js, Auth.js, Prisma or Stripe break, it goes out in the VibeBoot newsletter. Free, occasional, unsubscribe in one click.
> More free tools
All tools →- VibeBoot DoctorNewPaste any error or your package.json: diagnosis, fix and the rule that prevents it.
- middleware.ts → proxy.tsNewConvert Next.js middleware to Next.js 16 proxy.ts, Auth.js v5 included.
- AGENTS.md checkerNewCheck AGENTS.md for the Next.js 16 managed block, commands and guardrails.
- Access control checkerPaste a route or server action, see what anyone can call.
- Prisma error fixerP2002, P1001, P2021, did not initialize yet: cause and fix.
- Next.js error fixerServer Component, Suspense and hydration errors: cause and fix.
- Env leak checkerPaste your .env, see which secrets would ship to the browser.
- Stripe webhook fixerPaste a Stripe webhook error, get the cause and the fix.
- AGENTS.md generatorA ready AGENTS.md and CLAUDE.md for your Next.js stack.
- Cursor Rules PackFree downloadScoped .mdc rules for Next.js, Stripe webhooks, access control and more.
- Cursor rules generatorBuild one project.mdc rule for your exact stack.
- AI chatbot cost calculatorCost per message, Pro margin and a safe free-tier cap.