[GH-ISSUE #4614] Bug: database user record not present in user-create-after hook #27319

Closed
opened 2026-04-17 18:15:39 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @CarrettaRiccardo on GitHub (Sep 12, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/4614

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Add a hook after user creation, and in that hook try to get the matching user record in the db.
The user record is not found (using google sign in) or hook not executed (credentials register after verificationUrl. click)

Current vs. Expected behavior

The user record is not found in the db, or the hook not executed.

For reference, the account-create-after hook does make it possible to fetch the account record in the db.

What version of Better Auth are you using?

1.3.10-beta.3

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:40 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6041",
    "release": "24.6.0",
    "cpuCount": 12,
    "cpuModel": "Apple M4 Pro",
    "totalMemory": "24.00 GB",
    "freeMemory": "2.16 GB"
  },
  "node": {
    "version": "v22.18.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.5.2"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "^15.5.3"
    },
    {
      "name": "react",
      "version": "19.1.1"
    }
  ],
  "databases": [
    {
      "name": "postgres",
      "version": "^3.4.7"
    },
    {
      "name": "drizzle",
      "version": "^0.44.5"
    }
  ],
  "betterAuth": {
    "version": "1.0.0",
    "config": null
  }
}

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

Backend

Auth config (if applicable)

export const auth = betterAuth({
  // ...
  account: {
    encryptOAuthTokens: true,
    accountLinking: {
      enabled: true,
      trustedProviders: ["google"],
      allowDifferentEmails: false,
    },
  },
  socialProviders: {
    google: env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET ? {
      prompt: "select_account",
      clientId: env.GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET,
      mapProfileToUser: (profile) => {
        const username = toUsernameSlug(profile.email?.split("@")[0] || profile.name || "user");

        // ensure to set all non-nullable fields
        const user = {
          name: profile.name,
          email: profile.email,
          emailVerified: profile.email_verified,
          image: undefined,// do not set image
          username: username,
          displayUsername: username,
          role: UserRole.USER,
        }// satisfies Partial<AuthUser>;

        return user;
      },
    } : undefined,
  },
  // ...
  emailVerification: {
    sendOnSignUp: true,
    expiresIn: 60 * 60 * 1, // 1 HOUR
    autoSignInAfterVerification: true,
    sendVerificationEmail: async ({ user, url, token }, _request) => {
      // better-auth route
      const verificationUrl = absoluteUrl(`/api/auth/verify-email?token=${token}&callbackURL=${PageSettingsProfile()}`);

      using emailService = new EmailService();
      await emailService.sendMail(EmailTemplate.Register, {
        input: {
          name: user.name,
          link: verificationUrl,
        },
        to: user.email,
        locale: i18n.defaultLocale,
      });
    },
  },
  // ... 
  databaseHooks: {
    user: {
      create: {
        after: async (user, ctx) => {
          if (process.env.NODE_ENV === "development") {
            console.log("(DEV) auth user-create-after", user);
          }

          const createdUser = await db.select().from(tables.users).where(eq(tables.users.id, user.id));

          if (!createdUser) {
            console.warn("⚠️", "User not found");
            return;
          }

          // never reached code...
        },
      },
    },
  },
  // ...
};

Additional context

No response

Originally created by @CarrettaRiccardo on GitHub (Sep 12, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/4614 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Add a hook after user creation, and in that hook try to get the matching user record in the db. The user record is not found (using google sign in) or hook not executed (credentials register after verificationUrl. click) ### Current vs. Expected behavior The user record is not found in the db, or the hook not executed. For reference, the account-create-after hook does make it possible to fetch the account record in the db. ### What version of Better Auth are you using? 1.3.10-beta.3 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:40 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6041", "release": "24.6.0", "cpuCount": 12, "cpuModel": "Apple M4 Pro", "totalMemory": "24.00 GB", "freeMemory": "2.16 GB" }, "node": { "version": "v22.18.0", "env": "development" }, "packageManager": { "name": "npm", "version": "11.5.2" }, "frameworks": [ { "name": "next", "version": "^15.5.3" }, { "name": "react", "version": "19.1.1" } ], "databases": [ { "name": "postgres", "version": "^3.4.7" }, { "name": "drizzle", "version": "^0.44.5" } ], "betterAuth": { "version": "1.0.0", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript export const auth = betterAuth({ // ... account: { encryptOAuthTokens: true, accountLinking: { enabled: true, trustedProviders: ["google"], allowDifferentEmails: false, }, }, socialProviders: { google: env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET ? { prompt: "select_account", clientId: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET, mapProfileToUser: (profile) => { const username = toUsernameSlug(profile.email?.split("@")[0] || profile.name || "user"); // ensure to set all non-nullable fields const user = { name: profile.name, email: profile.email, emailVerified: profile.email_verified, image: undefined,// do not set image username: username, displayUsername: username, role: UserRole.USER, }// satisfies Partial<AuthUser>; return user; }, } : undefined, }, // ... emailVerification: { sendOnSignUp: true, expiresIn: 60 * 60 * 1, // 1 HOUR autoSignInAfterVerification: true, sendVerificationEmail: async ({ user, url, token }, _request) => { // better-auth route const verificationUrl = absoluteUrl(`/api/auth/verify-email?token=${token}&callbackURL=${PageSettingsProfile()}`); using emailService = new EmailService(); await emailService.sendMail(EmailTemplate.Register, { input: { name: user.name, link: verificationUrl, }, to: user.email, locale: i18n.defaultLocale, }); }, }, // ... databaseHooks: { user: { create: { after: async (user, ctx) => { if (process.env.NODE_ENV === "development") { console.log("(DEV) auth user-create-after", user); } const createdUser = await db.select().from(tables.users).where(eq(tables.users.id, user.id)); if (!createdUser) { console.warn("⚠️", "User not found"); return; } // never reached code... }, }, }, }, // ... }; ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-17 18:15:39 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 12, 2025):

This is a known issue in Better Auth: the user-create-after hook can be unreliable in certain flows like Google sign-in or credentials registration after email verification. In these cases, the hook may fire before the user record is fully committed to the database, or it may not fire at all, depending on the provider flow and async timing. The account-create-after hook is more reliable because it is triggered after DB persistence in all flows, which is why you see different behavior between the two hooks.

There are a few things to check and try:

  • For OAuth flows, make sure your user schema matches what Better Auth expects (including required fields like image), as schema mismatches can cause silent failures in user creation, resulting in missing records in hooks (source).
  • Some users have worked around this by introducing a short delay or queueing logic in the user-create-after hook before querying the DB, but this is not ideal (source).
  • There are also reports that verification hooks (and sometimes user hooks) are not triggered as expected when clicking the verification email URL, especially with autoSignInAfterVerification enabled (source).

At the moment, there is no configuration option to guarantee the user record will always be available in the user-create-after hook across all flows. If you need to reliably react to new user creation, consider using the account-create-after hook instead, or check the createdAt timestamp on the user to distinguish new signups from logins.

If you need more granular control, you may need to implement a custom workaround (like a post-processing queue) until the hook timing is made more consistent in future releases.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3285845040 --> @dosubot[bot] commented on GitHub (Sep 12, 2025): <!-- Answer --> This is a known issue in Better Auth: the `user-create-after` hook can be unreliable in certain flows like Google sign-in or credentials registration after email verification. In these cases, the hook may fire before the user record is fully committed to the database, or it may not fire at all, depending on the provider flow and async timing. The `account-create-after` hook is more reliable because it is triggered after DB persistence in all flows, which is why you see different behavior between the two hooks. There are a few things to check and try: - For OAuth flows, make sure your user schema matches what Better Auth expects (including required fields like `image`), as schema mismatches can cause silent failures in user creation, resulting in missing records in hooks [(source)](https://github.com/better-auth/better-auth/issues/1025). - Some users have worked around this by introducing a short delay or queueing logic in the `user-create-after` hook before querying the DB, but this is not ideal [(source)](https://github.com/better-auth/better-auth/issues/1512). - There are also reports that verification hooks (and sometimes user hooks) are not triggered as expected when clicking the verification email URL, especially with `autoSignInAfterVerification` enabled [(source)](https://github.com/better-auth/better-auth/issues/2840). At the moment, there is no configuration option to guarantee the user record will always be available in the `user-create-after` hook across all flows. If you need to reliably react to new user creation, consider using the `account-create-after` hook instead, or check the `createdAt` timestamp on the user to distinguish new signups from logins. If you need more granular control, you may need to implement a custom workaround (like a post-processing queue) until the hook timing is made more consistent in future releases. <!-- 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/40c7d309-cf3a-4386-be13-d6a859bf155e?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/40c7d309-cf3a-4386-be13-d6a859bf155e?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/40c7d309-cf3a-4386-be13-d6a859bf155e?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/40c7d309-cf3a-4386-be13-d6a859bf155e?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/40c7d309-cf3a-4386-be13-d6a859bf155e?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/40c7d309-cf3a-4386-be13-d6a859bf155e?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/40c7d309-cf3a-4386-be13-d6a859bf155e?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&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/4614)
Author
Owner

@frectonz commented on GitHub (Sep 12, 2025):

I was not able to reproduce this bug. Does the after hook work correctly when the user signs in wither other login methods like email + password or does the problem only exist for google oauth.

<!-- gh-comment-id:3286512418 --> @frectonz commented on GitHub (Sep 12, 2025): I was not able to reproduce this bug. Does the after hook work correctly when the user signs in wither other login methods like email + password or does the problem only exist for google oauth.
Author
Owner

@frectonz commented on GitHub (Sep 12, 2025):

Take a look at #4616 for the reproduction attempt.

<!-- gh-comment-id:3286584668 --> @frectonz commented on GitHub (Sep 12, 2025): Take a look at #4616 for the reproduction attempt.
Author
Owner

@CarrettaRiccardo commented on GitHub (Sep 12, 2025):

Apparently i made a mistake, don't know how i managed to not trigger the hook, maybe some other silent error.

Anyway there's still an issue --

  • register with Google -> user-create-after user record NOT present
  • register with Google -> account-create-after, user record NOT present (how??)
  • register with credentials & emailVerification with autoSignInAfterVerification: false -> hooks executed, user record present
  • register with credentials & emailVerification with autoSignInAfterVerification: true -> hooks executed, user record present

can't wrap my head at how it can create an account if the account's userId should reference the users.id table.

<!-- gh-comment-id:3287053832 --> @CarrettaRiccardo commented on GitHub (Sep 12, 2025): Apparently i made a mistake, don't know how i managed to not trigger the hook, maybe some other silent error. Anyway there's still an issue -- - register with Google -> ❌ `user-create-after` user record NOT present - register with Google -> ❌ `account-create-after`, user record NOT present (how??) - register with credentials & emailVerification with autoSignInAfterVerification: false -> ✅ hooks executed, user record present - register with credentials & emailVerification with autoSignInAfterVerification: true -> ✅ hooks executed, user record present can't wrap my head at how it can create an account if the `account`'s `userId` should reference the `users.id` table.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#27319