{"record":{"id":"79b357b13f4b60a1","repo":"nextauthjs/next-auth","slug":"credentialssignin","errorCode":null,"errorMessage":"CredentialsSignin","messagePattern":"CredentialsSignin","errorType":"exception","errorClass":"CredentialsSignin","httpStatus":null,"severity":"error","filePath":"packages/core/src/lib/actions/callback/index.ts","lineNumber":339,"sourceCode":"      }\n\n      // Callback URL is already verified at this point, so safe to use if specified\n      return { redirect: callbackUrl, cookies }\n    } else if (provider.type === \"credentials\" && method === \"POST\") {\n      const credentials = body ?? {}\n\n      // TODO: Forward the original request as is, instead of reconstructing it\n      Object.entries(query ?? {}).forEach(([k, v]) =>\n        url.searchParams.set(k, v)\n      )\n      const userFromAuthorize = await provider.authorize(\n        credentials,\n        // prettier-ignore\n        new Request(url, { headers, method, body: JSON.stringify(body) })\n      )\n      const user = userFromAuthorize\n\n      if (!user) throw new CredentialsSignin()\n      else user.id = user.id?.toString() ?? crypto.randomUUID()\n\n      const account = {\n        providerAccountId: user.id,\n        type: \"credentials\",\n        provider: provider.id,\n      } satisfies Account\n\n      const redirect = await handleAuthorized(\n        { user, account, credentials },\n        options\n      )\n      if (redirect) return { redirect, cookies }\n\n      const defaultToken = {\n        name: user.name,\n        email: user.email,\n        picture: user.image,","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/core/src/lib/actions/callback/index.ts#L321-L357","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check that `authorize()` explicitly returns the user object when credentials are valid, and null only on failure","Verify the credential comparison (e.g. bcrypt.compare) is awaited and actually checked before returning the user","Confirm the user lookup uses the correct identifier field and that the user exists in the database","On the client, handle the CredentialsSignin error code (fetch to /callback/credentials or signIn with redirect:false) to show a 'wrong credentials' message"],"exampleFix":"// before\nauthorize: async (credentials) => {\n  const user = await getUser(credentials.email)\n  bcrypt.compare(credentials.password, user.passwordHash) // result ignored\n  return user\n}\n// after\nauthorize: async (credentials) => {\n  const user = await getUser(credentials?.email)\n  if (!user) return null\n  const ok = await bcrypt.compare(credentials.password, user.passwordHash)\n  if (!ok) return null\n  return user\n}","handlingStrategy":"try-catch","validationCode":"// client-side guard before/at sign-in\nconst creds = { email, password }\nif (!creds.email || !creds.password) throw new Error(\"Missing credentials\")","typeGuard":"function hasCredentials(c: unknown): c is { email: string; password: string } {\n  return typeof c === \"object\" && c !== null &&\n    typeof (c as any).email === \"string\" && typeof (c as any).password === \"string\"\n}","tryCatchPattern":"const res = await signIn(\"credentials\", { redirect: false, email, password })\nif (res?.error === \"CredentialsSignin\") {\n  // show 'invalid username or password'\n}","preventionTips":["Always return null explicitly on failed auth in authorize(), and the user object on success","Await every async credential comparison before returning","Handle the CredentialsSignin error code on the client rather than crashing","Unit-test authorize() with valid, invalid, and missing inputs"],"tags":["auth","credentials","sign-in"],"backgroundTag":"credentials-signin-failed","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}