Clicking the change email verification URL does not change the email #2785

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

Originally created by @arimotearipo on GitHub (Jan 29, 2026).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Implement a simple configuration that allows changing email.
  2. Call authClient.changeEmail, from the frontend.
  3. The user.changeEmail.sendChangeEmailConfirmation function will be triggered that has the URL as the payload.
  4. Call the URL, and nothing happens.

Minimum reproducible code: https://github.com/arimotearipo/better-auth

Current vs. Expected behavior

Current behavior: The email remains the same after clicking on the URL

Expected behavior: The email should change to the new one.

What version of Better Auth are you using?

1.4.18

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:34 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T8112",
    "release": "25.2.0",
    "cpuCount": 8,
    "cpuModel": "Apple M2",
    "totalMemory": "8.00 GB",
    "freeMemory": "0.11 GB"
  },
  "node": {
    "version": "v22.19.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.3"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "16.1.6"
    },
    {
      "name": "react",
      "version": "19.2.3"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "^8.17.2"
    },
    {
      "name": "@prisma/client",
      "version": "^7.3.0"
    }
  ],
  "betterAuth": {
    "version": "^1.4.18",
    "config": {
      "baseURL": "http://localhost:3000",
      "advanced": {
        "disableOriginCheck": true
      },
      "user": {
        "changeEmail": {
          "enabled": true
        }
      },
      "emailAndPassword": {
        "enabled": true
      },
      "emailVerification": {
        "sendOnSignUp": true,
        "expiresIn": 86400
      }
    }
  }
}

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

Backend

Auth config (if applicable)

import { prisma } from "./prisma";
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";

export const auth = betterAuth({
  baseURL: process.env.BETTER_AUTH_BASE_URL || "http://localhost:3000",
  database: prismaAdapter(prisma, {
    provider: "postgresql",
  }),
  advanced: { disableOriginCheck: true },
  user: {
    changeEmail: {
      enabled: true,
      sendChangeEmailConfirmation: async (ctx, request) => {
        console.log(
          "Change email confirmation requested for user:",
          JSON.stringify(ctx, null, 2),
        );
      },
    },
  },
  emailAndPassword: {
    enabled: true,
  },
  emailVerification: {
    sendOnSignUp: true,
    expiresIn: 60 * 60 * 24,
    sendVerificationEmail: async (ctx, request) => {
      console.log(
        "Email verification requested for user:",
        JSON.stringify(ctx, null, 2),
      );
    },
  },
});

Additional context

No response

Originally created by @arimotearipo on GitHub (Jan 29, 2026). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Implement a simple configuration that allows changing email. 2. Call `authClient.changeEmail`, from the frontend. 3. The `user.changeEmail.sendChangeEmailConfirmation` function will be triggered that has the URL as the payload. 4. Call the URL, and nothing happens. Minimum reproducible code: https://github.com/arimotearipo/better-auth ### Current vs. Expected behavior Current behavior: The email remains the same after clicking on the URL Expected behavior: The email should change to the new one. ### What version of Better Auth are you using? 1.4.18 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:34 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T8112", "release": "25.2.0", "cpuCount": 8, "cpuModel": "Apple M2", "totalMemory": "8.00 GB", "freeMemory": "0.11 GB" }, "node": { "version": "v22.19.0", "env": "development" }, "packageManager": { "name": "npm", "version": "10.9.3" }, "frameworks": [ { "name": "next", "version": "16.1.6" }, { "name": "react", "version": "19.2.3" } ], "databases": [ { "name": "pg", "version": "^8.17.2" }, { "name": "@prisma/client", "version": "^7.3.0" } ], "betterAuth": { "version": "^1.4.18", "config": { "baseURL": "http://localhost:3000", "advanced": { "disableOriginCheck": true }, "user": { "changeEmail": { "enabled": true } }, "emailAndPassword": { "enabled": true }, "emailVerification": { "sendOnSignUp": true, "expiresIn": 86400 } } } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { prisma } from "./prisma"; import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; export const auth = betterAuth({ baseURL: process.env.BETTER_AUTH_BASE_URL || "http://localhost:3000", database: prismaAdapter(prisma, { provider: "postgresql", }), advanced: { disableOriginCheck: true }, user: { changeEmail: { enabled: true, sendChangeEmailConfirmation: async (ctx, request) => { console.log( "Change email confirmation requested for user:", JSON.stringify(ctx, null, 2), ); }, }, }, emailAndPassword: { enabled: true, }, emailVerification: { sendOnSignUp: true, expiresIn: 60 * 60 * 24, sendVerificationEmail: async (ctx, request) => { console.log( "Email verification requested for user:", JSON.stringify(ctx, null, 2), ); }, }, }); ``` ### Additional context _No response_
GiteaMirror added the bug label 2026-03-13 10:20:10 -05:00
Author
Owner

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

It sounds like you're experiencing an issue where clicking the change email verification URL does not update the email. This issue may be related to the verification process, as there have been recent discussions and changes regarding how email verification is handled in Better Auth.

Here are some troubleshooting steps to consider:

  1. Session Requirement: Ensure that your application requires an active session for the email change verification. A recent update mandates that a user must be logged in when attempting to perform changes related to email verification to prevent unauthorized access [6].

  2. Pending State for Email Changes: The implementation might be optimized to store the new email in a "pending" state until verification is successful. This means the email is only updated after the user clicks the verification link, and the system confirms their access to the new email [8].

  3. Check Configuration: Ensure that your changeEmail configuration within Better Auth is set properly. According to the latest updates, the email should be marked as verified immediately after clicking the verification link if it's implemented correctly [1].

  4. Debug Logs: Consider adding logging to monitor the flow when the verification link is clicked. This can help catch any failures or unexpected behavior in the process.

If these steps don’t resolve the issue, providing more insights into the error messages you receive or the logs generated could be beneficial. Alternatively, you might want to tag the Better Auth team members, Beka (@bekacru) and Max (@ping__), for further assistance.

Have you had a chance to review the email verification logs, or do you need help with any specific part of the configuration?

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 29, 2026): It sounds like you're experiencing an issue where clicking the change email verification URL does not update the email. This issue may be related to the verification process, as there have been recent discussions and changes regarding how email verification is handled in Better Auth. Here are some troubleshooting steps to consider: 1. **Session Requirement**: Ensure that your application requires an active session for the email change verification. A recent update mandates that a user must be logged in when attempting to perform changes related to email verification to prevent unauthorized access [[6]](https://github.com/better-auth/better-auth/pull/7160). 2. **Pending State for Email Changes**: The implementation might be optimized to store the new email in a "pending" state until verification is successful. This means the email is only updated after the user clicks the verification link, and the system confirms their access to the new email [[8]](https://github.com/better-auth/better-auth/pull/7198). 3. **Check Configuration**: Ensure that your `changeEmail` configuration within Better Auth is set properly. According to the latest updates, the email should be marked as verified immediately after clicking the verification link if it's implemented correctly [[1]](https://github.com/better-auth/better-auth/pull/5810). 4. **Debug Logs**: Consider adding logging to monitor the flow when the verification link is clicked. This can help catch any failures or unexpected behavior in the process. If these steps don’t resolve the issue, providing more insights into the error messages you receive or the logs generated could be beneficial. Alternatively, you might want to tag the Better Auth team members, Beka (@bekacru) and Max (@ping__), for further assistance. Have you had a chance to review the email verification logs, or do you need help with any specific part of the configuration? _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

@dosubot[bot] commented on GitHub (Jan 29, 2026):

Hi @arimotearipo! I'm Dosu and I’m helping the better-auth team.

The reason clicking the change email verification URL doesn't update the email is that your sendChangeEmailConfirmation function only logs to the console and doesn't actually send an email with the confirmation URL. For the flow to work, you need to implement sendChangeEmailConfirmation so it sends a real email to the user containing the provided URL. The backend expects the user to receive and click this link to proceed with the email change process, which is handled by the /verify-email endpoint and updates the user's email after the correct verification steps are completed (source).

If you're using Prisma v7, be aware of known compatibility issues where update operations (like changeEmail) may silently fail if your Prisma schema and config aren't manually updated after running the Better Auth CLI, or if model/field names don't match exactly (source). Double-check your schema and enable debug logging if you suspect this is the case.

In summary: implement actual email sending in sendChangeEmailConfirmation, ensure your /verify-email route is accessible, and check for Prisma v7 schema issues if the email still doesn't update after clicking the link.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

@dosubot[bot] commented on GitHub (Jan 29, 2026): <!-- Greeting --> Hi @arimotearipo! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The reason clicking the change email verification URL doesn't update the email is that your sendChangeEmailConfirmation function only logs to the console and doesn't actually send an email with the confirmation URL. For the flow to work, you need to implement sendChangeEmailConfirmation so it sends a real email to the user containing the provided URL. The backend expects the user to receive and click this link to proceed with the email change process, which is handled by the /verify-email endpoint and updates the user's email after the correct verification steps are completed ([source](https://github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts#L3-L538)). If you're using Prisma v7, be aware of known compatibility issues where update operations (like changeEmail) may silently fail if your Prisma schema and config aren't manually updated after running the Better Auth CLI, or if model/field names don't match exactly ([source](https://github.com/better-auth/better-auth/issues/6469)). Double-check your schema and enable debug logging if you suspect this is the case. In summary: implement actual email sending in sendChangeEmailConfirmation, ensure your /verify-email route is accessible, and check for Prisma v7 schema issues if the email still doesn't update after clicking the link. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=The%20reason%20clicking%20the%20change%20email%20verification%20URL%20doesn%27t%20update%20the%20email%20is%20that%20your%20sendChangeEmailConfirmation%20function%20only%20logs%20to%20the%20console%20and%20doesn%27t%20actually%20send%20an%20email%20with%20the%20confirmation%20URL.%20For%20the%20flow%20to%20work%2C%20you%20need%20to%20implement%20sendChangeEmailConfirmation%20so%20it%20sends%20a%20real%20email%20to%20the%20user%20containing%20the%20provided%20URL.%20The%20backend%20expects%20the%20user%20to%20receive%20and%20click%20this%20link%20to%20proceed%20with%20the%20email%20change%20process%2C%20which%20is%20handled%20by%20the%20/verify-email%20endpoint%20and%20updates%20the%20user%27s%20email%20after%20the%20correct%20verification%20steps%20are%20completed%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts%23L3-L538%29%29.%0A%0AIf%20you%27re%20using%20Prisma%20v7%2C%20be%20aware%20of%20known%20compatibility%20issues%20where%20update%20operations%20%28like%20changeEmail%29%20may%20silently%20fail%20if%20your%20Prisma%20schema%20and%20config%20aren%27t%20manually%20updated%20after%20running%20the%20Better%20Auth%20CLI%2C%20or%20if%20model/field%20names%20don%27t%20match%20exactly%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/6469%29%29.%20Double-check%20your%20schema%20and%20enable%20debug%20logging%20if%20you%20suspect%20this%20is%20the%20case.%0A%0AIn%20summary%3A%20implement%20actual%20email%20sending%20in%20sendChangeEmailConfirmation%2C%20ensure%20your%20/verify-email%20route%20is%20accessible%2C%20and%20check%20for%20Prisma%20v7%20schema%20issues%20if%20the%20email%20still%20doesn%27t%20update%20after%20clicking%20the%20link.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7689)
Author
Owner

@arimotearipo commented on GitHub (Jan 29, 2026):

Hi @arimotearipo! I'm Dosu and I’m helping the better-auth team.

The reason clicking the change email verification URL doesn't update the email is that your sendChangeEmailConfirmation function only logs to the console and doesn't actually send an email with the confirmation URL. For the flow to work, you need to implement sendChangeEmailConfirmation so it sends a real email to the user containing the provided URL. The backend expects the user to receive and click this link to proceed with the email change process, which is handled by the /verify-email endpoint and updates the user's email after the correct verification steps are completed (source).

If you're using Prisma v7, be aware of known compatibility issues where update operations (like changeEmail) may silently fail if your Prisma schema and config aren't manually updated after running the Better Auth CLI, or if model/field names don't match exactly (source). Double-check your schema and enable debug logging if you suspect this is the case.

In summary: implement actual email sending in sendChangeEmailConfirmation, ensure your /verify-email route is accessible, and check for Prisma v7 schema issues if the email still doesn't update after clicking the link.

To reply, just mention @dosu.

How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

The issue here is not whether user is receiving the email confirmation or not. The issue here is that the user's email doesn't change after clicking on the link. The reason I am just logging the URL to the console is to keep it simple for the purpose of minimally reproducing the issue. Regardless of whether the URL is received through an email or through the console logs, clicking on the URL should change the user's email.

@arimotearipo commented on GitHub (Jan 29, 2026): > Hi [@arimotearipo](https://github.com/arimotearipo)! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. > > The reason clicking the change email verification URL doesn't update the email is that your sendChangeEmailConfirmation function only logs to the console and doesn't actually send an email with the confirmation URL. For the flow to work, you need to implement sendChangeEmailConfirmation so it sends a real email to the user containing the provided URL. The backend expects the user to receive and click this link to proceed with the email change process, which is handled by the /verify-email endpoint and updates the user's email after the correct verification steps are completed ([source](https://github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts#L3-L538)). > > If you're using Prisma v7, be aware of known compatibility issues where update operations (like changeEmail) may silently fail if your Prisma schema and config aren't manually updated after running the Better Auth CLI, or if model/field names don't match exactly ([source](https://github.com/better-auth/better-auth/issues/6469)). Double-check your schema and enable debug logging if you suspect this is the case. > > In summary: implement actual email sending in sendChangeEmailConfirmation, ensure your /verify-email route is accessible, and check for Prisma v7 schema issues if the email still doesn't update after clicking the link. > > _To reply, just mention [@dosu](https://go.dosu.dev/dosubot)._ > > How did I do? [Good](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/d44c284a-a39b-4094-8b4c-5e4fe5c3c36e?feedback_type=other) > > [![Chat with Dosu](https://camo.githubusercontent.com/7c571478962a6b8a5d47cac74b6824c75e4f6731bba52377e6007f9150462923/68747470733a2f2f646f73752e6465762f646f73752d636861742d62616467652e737667)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github) [![Open in Cursor](https://camo.githubusercontent.com/bdf7dbded65ee585887f47e5fd6fa099ea0933088b4b03d02e83b4def50154d4/68747470733a2f2f646f73752e6465762f646f73752d696e2d637572736f722e737667)](https://cursor.com/link/prompt?text=The%20reason%20clicking%20the%20change%20email%20verification%20URL%20doesn%27t%20update%20the%20email%20is%20that%20your%20sendChangeEmailConfirmation%20function%20only%20logs%20to%20the%20console%20and%20doesn%27t%20actually%20send%20an%20email%20with%20the%20confirmation%20URL.%20For%20the%20flow%20to%20work%2C%20you%20need%20to%20implement%20sendChangeEmailConfirmation%20so%20it%20sends%20a%20real%20email%20to%20the%20user%20containing%20the%20provided%20URL.%20The%20backend%20expects%20the%20user%20to%20receive%20and%20click%20this%20link%20to%20proceed%20with%20the%20email%20change%20process%2C%20which%20is%20handled%20by%20the%20/verify-email%20endpoint%20and%20updates%20the%20user%27s%20email%20after%20the%20correct%20verification%20steps%20are%20completed%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts%23L3-L538%29%29.%0A%0AIf%20you%27re%20using%20Prisma%20v7%2C%20be%20aware%20of%20known%20compatibility%20issues%20where%20update%20operations%20%28like%20changeEmail%29%20may%20silently%20fail%20if%20your%20Prisma%20schema%20and%20config%20aren%27t%20manually%20updated%20after%20running%20the%20Better%20Auth%20CLI%2C%20or%20if%20model/field%20names%20don%27t%20match%20exactly%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/6469%29%29.%20Double-check%20your%20schema%20and%20enable%20debug%20logging%20if%20you%20suspect%20this%20is%20the%20case.%0A%0AIn%20summary%3A%20implement%20actual%20email%20sending%20in%20sendChangeEmailConfirmation%2C%20ensure%20your%20/verify-email%20route%20is%20accessible%2C%20and%20check%20for%20Prisma%20v7%20schema%20issues%20if%20the%20email%20still%20doesn%27t%20update%20after%20clicking%20the%20link.) [![Join Discord](https://camo.githubusercontent.com/7d2066700925db1e370d956834f9ea0e5bc92cecbe84963fbd2a363e45091b4e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6a6f696e2d3538363546323f6c6f676f3d646973636f7264266c6f676f436f6c6f723d7768697465266c6162656c3d)](https://go.dosu.dev/discord-bot) [![Share on X](https://camo.githubusercontent.com/a8bd7f3fcf3f5e3bd124eee9ae50ae2ac17b40971726665ac2121d48f9aee155/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f582d73686172652d626c61636b)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7689) The issue here is not whether user is receiving the email confirmation or not. The issue here is that the user's email doesn't change after clicking on the link. The reason I am just logging the URL to the console is to keep it simple for the purpose of minimally reproducing the issue. Regardless of whether the URL is received through an email or through the console logs, clicking on the URL should change the user's email.
Author
Owner

@dosubot[bot] commented on GitHub (Jan 29, 2026):

This is expected: clicking the first change email confirmation URL (the one sent to the current/old email) does not update the user's email. Instead, it triggers the backend to send a verification link to the new email address. Only after clicking the verification link sent to the new email will the backend actually update the user's email and mark it as verified. This two-step process is by design for security reasons and is confirmed by both the code and tests in Better Auth v1.4+ (source, source).

If you want the email to update after clicking the first link, you would need to enable the legacy/less secure flow (not recommended), or ensure your config allows immediate update for unverified emails. For verified users, the two-step flow is enforced.

If the verification email to the new address is not being sent, make sure your sendVerificationEmail function is implemented and actually sends the email. If it is sent and you click that link, but the email still doesn't update, check for Prisma v7 schema issues—model/field mismatches or missing @unique constraints can cause silent failures (source).

In summary: clicking the first link only triggers the second step; the actual email update happens after verifying the new email. This is intentional and matches the current Better Auth flow.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

@dosubot[bot] commented on GitHub (Jan 29, 2026): <!-- Answer --> This is expected: clicking the first change email confirmation URL (the one sent to the current/old email) does not update the user's email. Instead, it triggers the backend to send a verification link to the new email address. Only after clicking the verification link sent to the new email will the backend actually update the user's email and mark it as verified. This two-step process is by design for security reasons and is confirmed by both the code and tests in Better Auth v1.4+ ([source](https://github.com/better-auth/better-auth/pull/6088), [source](https://github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts#L207-L538)). If you want the email to update after clicking the first link, you would need to enable the legacy/less secure flow (not recommended), or ensure your config allows immediate update for unverified emails. For verified users, the two-step flow is enforced. If the verification email to the new address is not being sent, make sure your `sendVerificationEmail` function is implemented and actually sends the email. If it is sent and you click that link, but the email still doesn't update, check for Prisma v7 schema issues—model/field mismatches or missing `@unique` constraints can cause silent failures ([source](https://github.com/better-auth/better-auth/issues/6469)). In summary: clicking the first link only triggers the second step; the actual email update happens after verifying the new email. This is intentional and matches the current Better Auth flow. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20expected%3A%20clicking%20the%20first%20change%20email%20confirmation%20URL%20%28the%20one%20sent%20to%20the%20current/old%20email%29%20does%20not%20update%20the%20user%27s%20email.%20Instead%2C%20it%20triggers%20the%20backend%20to%20send%20a%20verification%20link%20to%20the%20new%20email%20address.%20Only%20after%20clicking%20the%20verification%20link%20sent%20to%20the%20new%20email%20will%20the%20backend%20actually%20update%20the%20user%27s%20email%20and%20mark%20it%20as%20verified.%20This%20two-step%20process%20is%20by%20design%20for%20security%20reasons%20and%20is%20confirmed%20by%20both%20the%20code%20and%20tests%20in%20Better%20Auth%20v1.4%2B%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/pull/6088%29%2C%20%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts%23L207-L538%29%29.%0A%0AIf%20you%20want%20the%20email%20to%20update%20after%20clicking%20the%20first%20link%2C%20you%20would%20need%20to%20enable%20the%20legacy/less%20secure%20flow%20%28not%20recommended%29%2C%20or%20ensure%20your%20config%20allows%20immediate%20update%20for%20unverified%20emails.%20For%20verified%20users%2C%20the%20two-step%20flow%20is%20enforced.%0A%0AIf%20the%20verification%20email%20to%20the%20new%20address%20is%20not%20being%20sent%2C%20make%20sure%20your%20%60sendVerificationEmail%60%20function%20is%20implemented%20and%20actually%20sends%20the%20email.%20If%20it%20is%20sent%20and%20you%20click%20that%20link%2C%20but%20the%20email%20still%20doesn%27t%20update%2C%20check%20for%20Prisma%20v7%20schema%20issues%E2%80%94model/field%20mismatches%20or%20missing%20%60%40unique%60%20constraints%20can%20cause%20silent%20failures%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/6469%29%29.%0A%0AIn%20summary%3A%20clicking%20the%20first%20link%20only%20triggers%20the%20second%20step%3B%20the%20actual%20email%20update%20happens%20after%20verifying%20the%20new%20email.%20This%20is%20intentional%20and%20matches%20the%20current%20Better%20Auth%20flow.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7689)
Author
Owner

@arimotearipo commented on GitHub (Jan 29, 2026):

This is expected: clicking the first change email confirmation URL (the one sent to the current/old email) does not update the user's email. Instead, it triggers the backend to send a verification link to the new email address. Only after clicking the verification link sent to the new email will the backend actually update the user's email and mark it as verified. This two-step process is by design for security reasons and is confirmed by both the code and tests in Better Auth v1.4+ (source, source).

If you want the email to update after clicking the first link, you would need to enable the legacy/less secure flow (not recommended), or ensure your config allows immediate update for unverified emails. For verified users, the two-step flow is enforced.

If the verification email to the new address is not being sent, make sure your sendVerificationEmail function is implemented and actually sends the email. If it is sent and you click that link, but the email still doesn't update, check for Prisma v7 schema issues—model/field mismatches or missing @unique constraints can cause silent failures (source).

In summary: clicking the first link only triggers the second step; the actual email update happens after verifying the new email. This is intentional and matches the current Better Auth flow.

To reply, just mention @dosu.

How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

If that is the case then I have also updated updateEmailWithoutVerification to true and yet the email still doesn't change.

Here is the full configuration:

export const auth = betterAuth({
  baseURL: process.env.BETTER_AUTH_BASE_URL || "http://localhost:3000",
  database: prismaAdapter(prisma, {
    provider: "postgresql",
  }),
  advanced: { disableOriginCheck: true },
  user: {
    changeEmail: {
      enabled: true,
      updateEmailWithoutVerification: true,
      sendChangeEmailConfirmation: async (ctx, request) => {
        console.log(
          "Change email confirmation requested for user:",
          JSON.stringify(ctx, null, 2),
        );
      },
    },
  },
  emailAndPassword: {
    enabled: true,
  },
  emailVerification: {
    sendOnSignUp: true,
    expiresIn: 60 * 60 * 24,
    sendVerificationEmail: async (ctx, request) => {
      console.log(
        "Email verification requested for user:",
        JSON.stringify(ctx, null, 2),
      );
    },
  },
});
@arimotearipo commented on GitHub (Jan 29, 2026): > This is expected: clicking the first change email confirmation URL (the one sent to the current/old email) does not update the user's email. Instead, it triggers the backend to send a verification link to the new email address. Only after clicking the verification link sent to the new email will the backend actually update the user's email and mark it as verified. This two-step process is by design for security reasons and is confirmed by both the code and tests in Better Auth v1.4+ ([source](https://github.com/better-auth/better-auth/pull/6088), [source](https://github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts#L207-L538)). > > If you want the email to update after clicking the first link, you would need to enable the legacy/less secure flow (not recommended), or ensure your config allows immediate update for unverified emails. For verified users, the two-step flow is enforced. > > If the verification email to the new address is not being sent, make sure your `sendVerificationEmail` function is implemented and actually sends the email. If it is sent and you click that link, but the email still doesn't update, check for Prisma v7 schema issues—model/field mismatches or missing `@unique` constraints can cause silent failures ([source](https://github.com/better-auth/better-auth/issues/6469)). > > In summary: clicking the first link only triggers the second step; the actual email update happens after verifying the new email. This is intentional and matches the current Better Auth flow. > > _To reply, just mention [@dosu](https://go.dosu.dev/dosubot)._ > > How did I do? [Good](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/a21f7117-d761-4b67-94f7-72fcdaa894a9?feedback_type=other) > > [![Chat with Dosu](https://camo.githubusercontent.com/7c571478962a6b8a5d47cac74b6824c75e4f6731bba52377e6007f9150462923/68747470733a2f2f646f73752e6465762f646f73752d636861742d62616467652e737667)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github) [![Open in Cursor](https://camo.githubusercontent.com/bdf7dbded65ee585887f47e5fd6fa099ea0933088b4b03d02e83b4def50154d4/68747470733a2f2f646f73752e6465762f646f73752d696e2d637572736f722e737667)](https://cursor.com/link/prompt?text=This%20is%20expected%3A%20clicking%20the%20first%20change%20email%20confirmation%20URL%20%28the%20one%20sent%20to%20the%20current/old%20email%29%20does%20not%20update%20the%20user%27s%20email.%20Instead%2C%20it%20triggers%20the%20backend%20to%20send%20a%20verification%20link%20to%20the%20new%20email%20address.%20Only%20after%20clicking%20the%20verification%20link%20sent%20to%20the%20new%20email%20will%20the%20backend%20actually%20update%20the%20user%27s%20email%20and%20mark%20it%20as%20verified.%20This%20two-step%20process%20is%20by%20design%20for%20security%20reasons%20and%20is%20confirmed%20by%20both%20the%20code%20and%20tests%20in%20Better%20Auth%20v1.4%2B%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/pull/6088%29%2C%20%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts%23L207-L538%29%29.%0A%0AIf%20you%20want%20the%20email%20to%20update%20after%20clicking%20the%20first%20link%2C%20you%20would%20need%20to%20enable%20the%20legacy/less%20secure%20flow%20%28not%20recommended%29%2C%20or%20ensure%20your%20config%20allows%20immediate%20update%20for%20unverified%20emails.%20For%20verified%20users%2C%20the%20two-step%20flow%20is%20enforced.%0A%0AIf%20the%20verification%20email%20to%20the%20new%20address%20is%20not%20being%20sent%2C%20make%20sure%20your%20%60sendVerificationEmail%60%20function%20is%20implemented%20and%20actually%20sends%20the%20email.%20If%20it%20is%20sent%20and%20you%20click%20that%20link%2C%20but%20the%20email%20still%20doesn%27t%20update%2C%20check%20for%20Prisma%20v7%20schema%20issues%E2%80%94model/field%20mismatches%20or%20missing%20%60%40unique%60%20constraints%20can%20cause%20silent%20failures%20%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/6469%29%29.%0A%0AIn%20summary%3A%20clicking%20the%20first%20link%20only%20triggers%20the%20second%20step%3B%20the%20actual%20email%20update%20happens%20after%20verifying%20the%20new%20email.%20This%20is%20intentional%20and%20matches%20the%20current%20Better%20Auth%20flow.) [![Join Discord](https://camo.githubusercontent.com/7d2066700925db1e370d956834f9ea0e5bc92cecbe84963fbd2a363e45091b4e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6a6f696e2d3538363546323f6c6f676f3d646973636f7264266c6f676f436f6c6f723d7768697465266c6162656c3d)](https://go.dosu.dev/discord-bot) [![Share on X](https://camo.githubusercontent.com/a8bd7f3fcf3f5e3bd124eee9ae50ae2ac17b40971726665ac2121d48f9aee155/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f582d73686172652d626c61636b)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7689) If that is the case then I have also updated `updateEmailWithoutVerification` to true and yet the email still doesn't change. Here is the full configuration: ```TS export const auth = betterAuth({ baseURL: process.env.BETTER_AUTH_BASE_URL || "http://localhost:3000", database: prismaAdapter(prisma, { provider: "postgresql", }), advanced: { disableOriginCheck: true }, user: { changeEmail: { enabled: true, updateEmailWithoutVerification: true, sendChangeEmailConfirmation: async (ctx, request) => { console.log( "Change email confirmation requested for user:", JSON.stringify(ctx, null, 2), ); }, }, }, emailAndPassword: { enabled: true, }, emailVerification: { sendOnSignUp: true, expiresIn: 60 * 60 * 24, sendVerificationEmail: async (ctx, request) => { console.log( "Email verification requested for user:", JSON.stringify(ctx, null, 2), ); }, }, }); ```
Author
Owner

@dosubot[bot] commented on GitHub (Jan 29, 2026):

Setting updateEmailWithoutVerification: true only allows immediate email updates for users whose current email is not verified. For accounts where emailVerified is true, Better Auth always enforces the secure two-step flow: clicking the first confirmation link (sent to the old email) just triggers a verification email to the new address, and only after clicking the verification link sent to the new email does the backend update the user's email and mark it as verified. This is intentional and cannot be bypassed by config for verified users see code and docs, see implementation.

If your test account is unverified and the email still doesn't update, check your Prisma schema for correct model/field names and @unique constraints—silent failures are common with Prisma v7 see issue. Enable debug logging in your Prisma adapter to catch any errors during update operations.

If you want to test the immediate update flow, make sure the user's emailVerified field is false in your database before triggering the change. For verified users, the two-step process is always enforced for security.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

@dosubot[bot] commented on GitHub (Jan 29, 2026): <!-- Answer --> Setting `updateEmailWithoutVerification: true` only allows immediate email updates for users whose current email is **not verified**. For accounts where `emailVerified` is true, Better Auth always enforces the secure two-step flow: clicking the first confirmation link (sent to the old email) just triggers a verification email to the new address, and only after clicking the verification link sent to the new email does the backend update the user's email and mark it as verified. This is intentional and cannot be bypassed by config for verified users [see code and docs](https://github.com/better-auth/better-auth/pull/6088), [see implementation](https://github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts#L207-L538). If your test account is unverified and the email still doesn't update, check your Prisma schema for correct model/field names and `@unique` constraints—silent failures are common with Prisma v7 [see issue](https://github.com/better-auth/better-auth/issues/6469). Enable debug logging in your Prisma adapter to catch any errors during update operations. If you want to test the immediate update flow, make sure the user's `emailVerified` field is false in your database before triggering the change. For verified users, the two-step process is always enforced for security. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/2e4698c3-fde5-43dc-a20b-5fb5b03e2471?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/2e4698c3-fde5-43dc-a20b-5fb5b03e2471?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/2e4698c3-fde5-43dc-a20b-5fb5b03e2471?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/2e4698c3-fde5-43dc-a20b-5fb5b03e2471?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/2e4698c3-fde5-43dc-a20b-5fb5b03e2471?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/2e4698c3-fde5-43dc-a20b-5fb5b03e2471?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/2e4698c3-fde5-43dc-a20b-5fb5b03e2471?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Setting%20%60updateEmailWithoutVerification%3A%20true%60%20only%20allows%20immediate%20email%20updates%20for%20users%20whose%20current%20email%20is%20%2A%2Anot%20verified%2A%2A.%20For%20accounts%20where%20%60emailVerified%60%20is%20true%2C%20Better%20Auth%20always%20enforces%20the%20secure%20two-step%20flow%3A%20clicking%20the%20first%20confirmation%20link%20%28sent%20to%20the%20old%20email%29%20just%20triggers%20a%20verification%20email%20to%20the%20new%20address%2C%20and%20only%20after%20clicking%20the%20verification%20link%20sent%20to%20the%20new%20email%20does%20the%20backend%20update%20the%20user%27s%20email%20and%20mark%20it%20as%20verified.%20This%20is%20intentional%20and%20cannot%20be%20bypassed%20by%20config%20for%20verified%20users%C2%A0%5Bsee%20code%20and%20docs%5D%28https%3A//github.com/better-auth/better-auth/pull/6088%29%2C%20%5Bsee%20implementation%5D%28https%3A//github.com/better-auth/better-auth/blob/d99d3ceae647ceb27aceb1b35aa7d5ca122d2a94/packages/better-auth/src/api/routes/email-verification.ts%23L207-L538%29.%0A%0AIf%20your%20test%20account%20is%20unverified%20and%20the%20email%20still%20doesn%27t%20update%2C%20check%20your%20Prisma%20schema%20for%20correct%20model/field%20names%20and%20%60%40unique%60%20constraints%E2%80%94silent%20failures%20are%20common%20with%20Prisma%20v7%C2%A0%5Bsee%20issue%5D%28https%3A//github.com/better-auth/better-auth/issues/6469%29.%20Enable%20debug%20logging%20in%20your%20Prisma%20adapter%20to%20catch%20any%20errors%20during%20update%20operations.%0A%0AIf%20you%20want%20to%20test%20the%20immediate%20update%20flow%2C%20make%20sure%20the%20user%27s%20%60emailVerified%60%20field%20is%20false%20in%20your%20database%20before%20triggering%20the%20change.%20For%20verified%20users%2C%20the%20two-step%20process%20is%20always%20enforced%20for%20security.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/7689)
Author
Owner

@arimotearipo commented on GitHub (Jan 29, 2026):

OK now I understand. Thank you for the explanation.

@arimotearipo commented on GitHub (Jan 29, 2026): OK now I understand. Thank you for the explanation.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2785