nextauthjs/next-auth · error · CredentialsSignin

CredentialsSignin

Error message

CredentialsSignin

What it means

CredentialsSignin is thrown during a credentials provider sign-in when the provider's `authorize` callback resolves to a falsy user (null/undefined). Auth.js treats a falsy return as 'these credentials did not match any user' and aborts the callback flow, which surfaces to the client as the CredentialsSignin error code. It exists so applications can distinguish failed credential checks from other route errors.

Source

Thrown at packages/core/src/lib/actions/callback/index.ts:339

      }

      // Callback URL is already verified at this point, so safe to use if specified
      return { redirect: callbackUrl, cookies }
    } else if (provider.type === "credentials" && method === "POST") {
      const credentials = body ?? {}

      // TODO: Forward the original request as is, instead of reconstructing it
      Object.entries(query ?? {}).forEach(([k, v]) =>
        url.searchParams.set(k, v)
      )
      const userFromAuthorize = await provider.authorize(
        credentials,
        // prettier-ignore
        new Request(url, { headers, method, body: JSON.stringify(body) })
      )
      const user = userFromAuthorize

      if (!user) throw new CredentialsSignin()
      else user.id = user.id?.toString() ?? crypto.randomUUID()

      const account = {
        providerAccountId: user.id,
        type: "credentials",
        provider: provider.id,
      } satisfies Account

      const redirect = await handleAuthorized(
        { user, account, credentials },
        options
      )
      if (redirect) return { redirect, cookies }

      const defaultToken = {
        name: user.name,
        email: user.email,
        picture: user.image,

View on GitHub (pinned to a1a16a5a77)

Solutions

  1. Check that `authorize()` explicitly returns the user object when credentials are valid, and null only on failure
  2. Verify the credential comparison (e.g. bcrypt.compare) is awaited and actually checked before returning the user
  3. Confirm the user lookup uses the correct identifier field and that the user exists in the database
  4. On the client, handle the CredentialsSignin error code (fetch to /callback/credentials or signIn with redirect:false) to show a 'wrong credentials' message

Example fix

// before
authorize: async (credentials) => {
  const user = await getUser(credentials.email)
  bcrypt.compare(credentials.password, user.passwordHash) // result ignored
  return user
}
// after
authorize: async (credentials) => {
  const user = await getUser(credentials?.email)
  if (!user) return null
  const ok = await bcrypt.compare(credentials.password, user.passwordHash)
  if (!ok) return null
  return user
}
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side guard before/at sign-in
const creds = { email, password }
if (!creds.email || !creds.password) throw new Error("Missing credentials")

Type guard

function hasCredentials(c: unknown): c is { email: string; password: string } {
  return typeof c === "object" && c !== null &&
    typeof (c as any).email === "string" && typeof (c as any).password === "string"
}

Try / catch

const res = await signIn("credentials", { redirect: false, email, password })
if (res?.error === "CredentialsSignin") {
  // show 'invalid username or password'
}

Prevention

When it happens

Trigger: A POST to /api/auth/callback/credentials where the credentials provider's `authorize()` returns null or undefined — e.g. the username/password lookup found no matching user, a password comparison (bcrypt/argon2) failed, or the developer forgot to return the user object on success.

Common situations: Wrong password entered by the user; `authorize` queries the DB with the wrong field (email vs username); missing await on the DB lookup so a promise resolves unexpectedly; authorize returns an object without running credential verification (in dev prototypes); env vars for the database not set so the lookup returns nothing.

Related errors


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