auth.api.verifyPhoneNumber returns USER_NOT_FOUND #2789

Closed
opened 2026-03-13 10:20:21 -05:00 by GiteaMirror · 7 comments
Owner

Originally created by @DanMossa on GitHub (Jan 30, 2026).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Setup auth.ts with phoneNumber plugin.
  2. Create a new user with
  await auth.api.sendPhoneNumberOTP({
    body: { phoneNumber: "+10001112222" },
    asResponse: true,
  });

I see a new row in verification

[
	{
		"id": "DDQy2qOYhUeZK3YWCdvsH6D7TCANNuIZ",
		"identifier": "+10001112222",
		"value": "747786:0",
		"expiresAt": "2026-01-30 02:13:14.08+00",
		"createdAt": "2026-01-30 02:08:14.08+00",
		"updatedAt": "2026-01-30 02:08:14.08+00"
	}
]

I then call

  const verifyPhoneNumberResponse = await auth.api.verifyPhoneNumber({
    body: {
      phoneNumber: patientPhoneNumber,
      code: "random_doesn't_matter", // doesn't matter because my `verifyOTP` method in `auth.ts` returns true
      disableSession: false,
      updatePhoneNumber: true,
    },
    asResponse: true,
  });

And the verifyPhoneNumberResponse value is

{code: 'USER_NOT_FOUND', message: 'User not found'}

I'm not sure what I'm doing wrong.

Current vs. Expected behavior

I expect a user to be created in the user row after I call auth.api.verifyPhoneNumber

What version of Better Auth are you using?

1.4.18

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 24.3.0: Thu Jan  2 20:24:23 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T6020",
    "release": "24.3.0",
    "cpuCount": 10,
    "cpuModel": "Apple M2 Pro",
    "totalMemory": "32.00 GB",
    "freeMemory": "0.09 GB"
  },
  "node": {
    "version": "v24.9.0",
    "env": "development"
  },
  "packageManager": {
    "name": "pnpm",
    "version": "10.28.1"
  },
  "frameworks": [
    {
      "name": "astro",
      "version": "^5.16.7"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "^8.16.3"
    },
    {
      "name": "kysely",
      "version": "^0.28.9"
    }
  ],
  "betterAuth": {
    "version": "^1.4.18",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { sendVerifyAccountEmail } from "@features/email/email-service";
import { betterAuth } from "better-auth";
import { phoneNumber, apiKey } from "better-auth/plugins";
import { Pool } from "pg";

export const auth = betterAuth({
  baseURL: import.meta.env?.BETTER_AUTH_BASE_URL ?? process.env.BETTER_AUTH_BASE_URL,
  database: new Pool({
    host: import.meta.env?.POSTGRES_HOST ?? process.env.POSTGRES_HOST,
    database: import.meta.env?.POSTGRES_DATABASE ?? process.env.POSTGRES_DATABASE,
    user: import.meta.env?.POSTGRES_USER ?? process.env.POSTGRES_USER,
    password: import.meta.env?.POSTGRES_PASSWORD ?? process.env.POSTGRES_PASSWORD,
    ssl: {
      rejectUnauthorized: true,
    },
  }),
  user: {
    fields: {
      name: "firstName",
    },
    additionalFields: {
      middleName: {
        required: false,
        type: "string",
        input: true,
      },
      lastName: {
        required: true,
        type: "string",
        input: true,
      },
      dateOfBirth: {
        required: true,
        type: "date",
        input: true,
      },
      timeZone: {
        required: false,
        type: "string",
        input: true,
      },
    },
    changeEmail: {
      enabled: true,
      updateEmailWithoutVerification: false,
    },
  },
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: true,
    minPasswordLength: 6,
  },
  emailVerification: {
    sendVerificationEmail: async ({ user, url, token }) => {
      await sendVerifyAccountEmail({ user, url, token });
    },
    autoSignInAfterVerification: true,
  },
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60, // 5 minutes
      strategy: "jwe",
    },
  },
  plugins: [
    apiKey(),
    phoneNumber({
      sendOTP: ({ phoneNumber, code }) => {
        //Switch to using Twilio eventually
        console.dir({
          phoneNumber,
          code,
          text: `Your verification code is ${code}`,
        });
      },

      verifyOTP: async ({ phoneNumber, code }) => {
        console.log({ phoneNumber, code });
        return true; // We'll remove this method once Twilio can send sms messages
      },
      // requireVerification: false, // We'll set this to true once we can actually send people text messages
      signUpOnVerification: {
        getTempEmail: (phoneNumber) => {
          console.log("getTEmpEmail");
          return `temporary-${phoneNumber}@anglerhealth.com`;
        },
        getTempName: (phoneNumber) => {
          console.log("getTempName");
          return phoneNumber; //by default, it will use the phone number as the name
        },
      },
    }),
  ],
});

Additional context

No response

Originally created by @DanMossa on GitHub (Jan 30, 2026). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Setup auth.ts with phoneNumber plugin. 2. Create a new user with ```typescript await auth.api.sendPhoneNumberOTP({ body: { phoneNumber: "+10001112222" }, asResponse: true, }); ``` I see a new row in `verification` ```json [ { "id": "DDQy2qOYhUeZK3YWCdvsH6D7TCANNuIZ", "identifier": "+10001112222", "value": "747786:0", "expiresAt": "2026-01-30 02:13:14.08+00", "createdAt": "2026-01-30 02:08:14.08+00", "updatedAt": "2026-01-30 02:08:14.08+00" } ] ``` I then call ```typescript const verifyPhoneNumberResponse = await auth.api.verifyPhoneNumber({ body: { phoneNumber: patientPhoneNumber, code: "random_doesn't_matter", // doesn't matter because my `verifyOTP` method in `auth.ts` returns true disableSession: false, updatePhoneNumber: true, }, asResponse: true, }); ``` And the `verifyPhoneNumberResponse` value is ```json {code: 'USER_NOT_FOUND', message: 'User not found'} ``` I'm not sure what I'm doing wrong. ### Current vs. Expected behavior I expect a user to be created in the user row after I call `auth.api.verifyPhoneNumber` ### What version of Better Auth are you using? 1.4.18 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 24.3.0: Thu Jan 2 20:24:23 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T6020", "release": "24.3.0", "cpuCount": 10, "cpuModel": "Apple M2 Pro", "totalMemory": "32.00 GB", "freeMemory": "0.09 GB" }, "node": { "version": "v24.9.0", "env": "development" }, "packageManager": { "name": "pnpm", "version": "10.28.1" }, "frameworks": [ { "name": "astro", "version": "^5.16.7" } ], "databases": [ { "name": "pg", "version": "^8.16.3" }, { "name": "kysely", "version": "^0.28.9" } ], "betterAuth": { "version": "^1.4.18", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { sendVerifyAccountEmail } from "@features/email/email-service"; import { betterAuth } from "better-auth"; import { phoneNumber, apiKey } from "better-auth/plugins"; import { Pool } from "pg"; export const auth = betterAuth({ baseURL: import.meta.env?.BETTER_AUTH_BASE_URL ?? process.env.BETTER_AUTH_BASE_URL, database: new Pool({ host: import.meta.env?.POSTGRES_HOST ?? process.env.POSTGRES_HOST, database: import.meta.env?.POSTGRES_DATABASE ?? process.env.POSTGRES_DATABASE, user: import.meta.env?.POSTGRES_USER ?? process.env.POSTGRES_USER, password: import.meta.env?.POSTGRES_PASSWORD ?? process.env.POSTGRES_PASSWORD, ssl: { rejectUnauthorized: true, }, }), user: { fields: { name: "firstName", }, additionalFields: { middleName: { required: false, type: "string", input: true, }, lastName: { required: true, type: "string", input: true, }, dateOfBirth: { required: true, type: "date", input: true, }, timeZone: { required: false, type: "string", input: true, }, }, changeEmail: { enabled: true, updateEmailWithoutVerification: false, }, }, emailAndPassword: { enabled: true, requireEmailVerification: true, minPasswordLength: 6, }, emailVerification: { sendVerificationEmail: async ({ user, url, token }) => { await sendVerifyAccountEmail({ user, url, token }); }, autoSignInAfterVerification: true, }, session: { cookieCache: { enabled: true, maxAge: 5 * 60, // 5 minutes strategy: "jwe", }, }, plugins: [ apiKey(), phoneNumber({ sendOTP: ({ phoneNumber, code }) => { //Switch to using Twilio eventually console.dir({ phoneNumber, code, text: `Your verification code is ${code}`, }); }, verifyOTP: async ({ phoneNumber, code }) => { console.log({ phoneNumber, code }); return true; // We'll remove this method once Twilio can send sms messages }, // requireVerification: false, // We'll set this to true once we can actually send people text messages signUpOnVerification: { getTempEmail: (phoneNumber) => { console.log("getTEmpEmail"); return `temporary-${phoneNumber}@anglerhealth.com`; }, getTempName: (phoneNumber) => { console.log("getTempName"); return phoneNumber; //by default, it will use the phone number as the name }, }, }), ], }); ``` ### Additional context _No response_
Author
Owner

@better-auth-agent[bot] commented on GitHub (Jan 30, 2026):

The USER_NOT_FOUND error you're experiencing when calling auth.api.verifyPhoneNumber generally means that the system cannot find the user associated with the provided phone number during the verification process. Here are a few things to check that might help resolve this:

  1. User Creation: Ensure that the user is being successfully created when you call sendPhoneNumberOTP() prior to the verification. The user should exist in your database.

  2. Phone Number Format: Confirm that the patientPhoneNumber you are passing in is formatted correctly and matches what is stored. It should be identical to the phone number used during the OTP sending process.

  3. Temporary User Creation: If using temporary user creation logic (as might be suggested by your signUpOnVerification configuration), make sure that the flow allows the user to persist in the database before verification.

  4. Database Check: Verify the user in the database directly to ensure they exist with the correct number prior to the verification call.

It's also worth reviewing any documentation or known issues related to this error. There may be specific configurations or steps needed for the phone number verification process that are not immediately evident.

If you're still facing issues, feel free to tag @bekacru or @ping__ from the Better Auth team for more specialized assistance! What have you tried so far based on these pointers?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

@better-auth-agent[bot] commented on GitHub (Jan 30, 2026): The `USER_NOT_FOUND` error you're experiencing when calling `auth.api.verifyPhoneNumber` generally means that the system cannot find the user associated with the provided phone number during the verification process. Here are a few things to check that might help resolve this: 1. **User Creation**: Ensure that the user is being successfully created when you call `sendPhoneNumberOTP()` prior to the verification. The user should exist in your database. 2. **Phone Number Format**: Confirm that the `patientPhoneNumber` you are passing in is formatted correctly and matches what is stored. It should be identical to the phone number used during the OTP sending process. 3. **Temporary User Creation**: If using temporary user creation logic (as might be suggested by your `signUpOnVerification` configuration), make sure that the flow allows the user to persist in the database before verification. 4. **Database Check**: Verify the user in the database directly to ensure they exist with the correct number prior to the verification call. It's also worth reviewing any documentation or known issues related to this error. There may be specific configurations or steps needed for the phone number verification process that are not immediately evident. If you're still facing issues, feel free to tag @bekacru or @ping__ from the Better Auth team for more specialized assistance! What have you tried so far based on these pointers? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@DanMossa commented on GitHub (Jan 30, 2026):

sendPhoneNumberOTP() is not creating a user in my database. is that because I have additional fields that are required in my database since I updated auth.ts? How can I make sendPhoneNumberOTP create value for those fields? @Bekacru

@DanMossa commented on GitHub (Jan 30, 2026): sendPhoneNumberOTP() is not creating a user in my database. is that because I have additional fields that are required in my database since I updated `auth.ts`? How can I make sendPhoneNumberOTP create value for those fields? @Bekacru
Author
Owner

@bytaesu commented on GitHub (Jan 30, 2026):

Hi @DanMossa,

For new users, it will work fine if you omit updatePhoneNumber option.

updatePhoneNumber option is used for already authenticated users. I've clarified the documentation accordingly 🙂

@bytaesu commented on GitHub (Jan 30, 2026): Hi @DanMossa, For new users, it will work fine if you omit `updatePhoneNumber` option. `updatePhoneNumber` option is used for already authenticated users. I've clarified the documentation accordingly 🙂
Author
Owner

@bytaesu commented on GitHub (Jan 30, 2026):

If the issue persists, feel free to mention me 😄

@bytaesu commented on GitHub (Jan 30, 2026): If the issue persists, feel free to mention me 😄
Author
Owner

@DanMossa commented on GitHub (Jan 30, 2026):

@bytaesu Thank you so much for the quick reply.
I'm no longer getting USER_NOT_FOUND, but am instead getting an error thrown that says

Error creating user: error: null value in column "lastName" of relation "user" violates not-null constraint

And this is because I have a required lastName as an additional field.
Is there anyway to specific those?

@DanMossa commented on GitHub (Jan 30, 2026): @bytaesu Thank you so much for the quick reply. I'm no longer getting `USER_NOT_FOUND`, but am instead getting an error thrown that says ``` Error creating user: error: null value in column "lastName" of relation "user" violates not-null constraint ``` And this is because I have a required `lastName` as an additional field. Is there anyway to specific those?
Author
Owner

@bytaesu commented on GitHub (Jan 30, 2026):

Hi @DanMossa,

Although there's a type-level limitation in fully propagating types to plugin endpoints, I've opened this PR to support the runtime behavior 🧐

@bytaesu commented on GitHub (Jan 30, 2026): Hi @DanMossa, Although there's a type-level limitation in fully propagating types to plugin endpoints, I've opened this PR to support the runtime behavior 🧐
Author
Owner

@DanMossa commented on GitHub (Jan 30, 2026):

You are God's gift. Thank you!

@DanMossa commented on GitHub (Jan 30, 2026): You are God's gift. Thank you!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2789