Email & password sign-in fails for adapters that don’t yet support joins #2462

Closed
opened 2026-03-13 09:56:17 -05:00 by GiteaMirror · 5 comments
Owner

Originally created by @Livog on GitHub (Dec 5, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Use better-auth 1.4.x with a custom adapter that does not support joins (in my case, the Payload adapter, which I co-maintain).
  2. Configure email/password authentication.
  3. Make sure the adapter is configured without join support (e.g. join is false/not implemented).
  4. Attempt to sign in with a valid email + password.
  5. The sign-in route calls:
const user = await ctx.context.internalAdapter.findUserByEmail(email, {
  includeAccounts: true,
});
  1. This forces join-related behavior inside the internal adapter and eventually leads to code that expects a joined result shaped like:
const { account: accounts, ...user } = result;
  1. Because the adapter does not support joins and does not return an account join, the credentials sign-in flow fails.

Current vs. Expected behavior

Current behavior

findUserByEmail is called with includeAccounts: true during email and password sign in, regardless of whether experimental.join is enabled.

Expected behavior

includeAccounts: true should only be passed to findUserByEmail when experimental.join is set to true in the betterAuth options. For setups where experimental.join is false, the call should not include includeAccounts, and email and password sign in should work with adapters that do not implement joins.

What version of Better Auth are you using?

1.4.5

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:55 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6031",
    "release": "24.6.0",
    "cpuCount": 16,
    "cpuModel": "Apple M3 Max",
    "totalMemory": "128.00 GB",
    "freeMemory": "2.23 GB"
  },
  "node": {
    "version": "v22.14.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.4"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "^15.4.8"
    },
    {
      "name": "react",
      "version": "^19.2.1"
    }
  ],
  "databases": null,
  "betterAuth": {
    "version": "^1.4.5",
    "config": null
  }
}

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

Package, Documentation

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
});

Additional context

I fixed it for payload-auth in this commit for payload-auth:
96880dc909

f61a5f4be7/packages/better-auth/src/api/routes/sign-in.ts (L451-L453)
f61a5f4be7/packages/better-auth/src/db/internal-adapter.ts (L740-L760)

Originally created by @Livog on GitHub (Dec 5, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Use better-auth 1.4.x with a custom adapter that does not support joins (in my case, the Payload adapter, which I co-maintain). 2. Configure email/password authentication. 3. Make sure the adapter is configured without join support (e.g. join is false/not implemented). 4. Attempt to sign in with a valid email + password. 5. The sign-in route calls: ```ts const user = await ctx.context.internalAdapter.findUserByEmail(email, { includeAccounts: true, }); ``` 6. This forces join-related behavior inside the internal adapter and eventually leads to code that expects a joined result shaped like: ```ts const { account: accounts, ...user } = result; ``` 7. Because the adapter does not support joins and does not return an account join, the credentials sign-in flow fails. ### Current vs. Expected behavior **Current behavior** findUserByEmail is called with includeAccounts: true during email and password sign in, regardless of whether experimental.join is enabled. **Expected behavior** includeAccounts: true should only be passed to findUserByEmail when experimental.join is set to true in the betterAuth options. For setups where experimental.join is false, the call should not include includeAccounts, and email and password sign in should work with adapters that do not implement joins. ### What version of Better Auth are you using? 1.4.5 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:55 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6031", "release": "24.6.0", "cpuCount": 16, "cpuModel": "Apple M3 Max", "totalMemory": "128.00 GB", "freeMemory": "2.23 GB" }, "node": { "version": "v22.14.0", "env": "development" }, "packageManager": { "name": "npm", "version": "10.9.4" }, "frameworks": [ { "name": "next", "version": "^15.4.8" }, { "name": "react", "version": "^19.2.1" } ], "databases": null, "betterAuth": { "version": "^1.4.5", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Package, Documentation ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, }); ``` ### Additional context I fixed it for payload-auth in this commit for payload-auth: https://github.com/payload-auth/payload-auth/pull/114/commits/96880dc90934cbd172ad3380e1d4504a0df5d87f https://github.com/better-auth/better-auth/blob/f61a5f4be7748f1dca9d23263b62173b4dc1c378/packages/better-auth/src/api/routes/sign-in.ts#L451-L453 https://github.com/better-auth/better-auth/blob/f61a5f4be7748f1dca9d23263b62173b4dc1c378/packages/better-auth/src/db/internal-adapter.ts#L740-L760
GiteaMirror added the adapter label 2026-03-13 09:56:17 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Dec 5, 2025):

Thanks for reporting this! It looks like the email/password sign‐in flow is currently always passing includeAccounts: true into findUserByEmail, even when your adapter isn’t set up to handle joins. To resolve this, you can either update your adapter to support joins (and return the expected joined result with an “account” property) or configure Better Auth with experimental.join set to false so that the call omits includeAccounts and avoids triggering the join logic. This issue was recently addressed in the codebase, where changes to the sign‐in route ensure that includeAccounts: true is only used when experimental.join is enabled [1, 2].

Have you had a chance to try toggling the experimental.join flag in your configuration? If not, give that a try and let us know how it goes. Tag @better-auth-agent if you would like an answer from the Agent.

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 (Dec 5, 2025): Thanks for reporting this! It looks like the email/password sign‐in flow is currently always passing includeAccounts: true into findUserByEmail, even when your adapter isn’t set up to handle joins. To resolve this, you can either update your adapter to support joins (and return the expected joined result with an “account” property) or configure Better Auth with experimental.join set to false so that the call omits includeAccounts and avoids triggering the join logic. This issue was recently addressed in the codebase, where changes to the sign‐in route ensure that includeAccounts: true is only used when experimental.join is enabled [1, 2]. Have you had a chance to try toggling the experimental.join flag in your configuration? If not, give that a try and let us know how it goes. Tag @better-auth-agent if you would like an answer from the Agent. _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/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@ping-maxwell commented on GitHub (Dec 7, 2025):

If you don't enable exerpimental.joins than better-auth automatically falls-back to normal queries.

You may look at the code and see the joins object but it doesn't actually mean it's using joins under the hood, there is something else going on under the hood.

@ping-maxwell commented on GitHub (Dec 7, 2025): If you don't enable `exerpimental.joins` than better-auth automatically falls-back to normal queries. You may look at the code and see the `joins` object but it doesn't actually mean it's using joins under the hood, there is something else going on under the hood.
Author
Owner

@ping-maxwell commented on GitHub (Dec 7, 2025):

Is your custom adapter built on top of the adapter factory? Because that is required.

@ping-maxwell commented on GitHub (Dec 7, 2025): Is your custom adapter built on top of the adapter factory? Because that is required.
Author
Owner

@Livog commented on GitHub (Dec 7, 2025):

If you don't enable exerpimental.joins than better-auth automatically falls-back to normal queries.

You may look at the code and see the joins object but it doesn't actually mean it's using joins under the hood, there is something else going on under the hood.

We have not touched that setting in our better auth configs.

Is your custom adapter built on top of the adapter factory? Because that is required.

It's not, maybe that is the issue then, I will look into this. I'm guessing it will remove joins then before calling the adatper?

@Livog commented on GitHub (Dec 7, 2025): > If you don't enable `exerpimental.joins` than better-auth automatically falls-back to normal queries. > > You may look at the code and see the `joins` object but it doesn't actually mean it's using joins under the hood, there is something else going on under the hood. We have not touched that setting in our better auth configs. > Is your custom adapter built on top of the adapter factory? Because that is required. It's not, maybe that is the issue then, I will look into this. I'm guessing it will remove joins then before calling the adatper?
Author
Owner

@ping-maxwell commented on GitHub (Dec 7, 2025):

It's not, maybe that is the issue then, I will look into this.

Yeah all adapters are required to use the adapter factory.

I'm guessing it will remove joins then before calling the adatper?

Yes, it detects if you have experimental joins enabled or not, and will fallback to using several normal queries and combine the results together to form the same results as though joins were performed.

@ping-maxwell commented on GitHub (Dec 7, 2025): > It's not, maybe that is the issue then, I will look into this. Yeah all adapters are required to use the adapter factory. > I'm guessing it will remove joins then before calling the adatper? Yes, it detects if you have experimental joins enabled or not, and will fallback to using several normal queries and combine the results together to form the same results as though joins were performed.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2462