Defaulting Kysley transaction to true with 1.3.10+ breaks Cloudflare D1 #1953

Closed
opened 2026-03-13 09:15:37 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @joshualyon on GitHub (Sep 17, 2025).

Originally assigned to: @himself65 on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  • Use better-auth 1.3.10 or newer
  • Use the kysley-d1 (or @atinux/kysely-d1) D1Dialect
  • Attempt creating a new user with a social login

Current vs. Expected behavior

After getting redirected from the social login back to the better-auth /api/auth/callback/{provider} endpoint, when better-auth attempts to create a new user with a social login with the D1Dialect, it results in a failure of unable_to_create_user

Social logins should continue to work in 1.3.10+ and newer even if this means explicitly passing in a transaction: false or similar to the database options configuration.

Unfortunately, the internal createKyselyAdapter(config) that is called only returns the kysely and databaseType configuration options, so if you've explicitly passed transaction: false, it doesn't actually get passed forward to the final created better-auth adapter.

4b83fa835c/packages/better-auth/src/adapters/kysely-adapter/dialect.ts (L53-L75)

So when the getAdapter() is called in db/utils.ts it calls createKyselyAdapter() and effectively drops all the database configuration except for db, type, and debugLogs:
4b83fa835c/packages/better-auth/src/db/utils.ts (L9-L36)

Critically, the transaction option is not passed forward to the kysleyAdapter() creation call even though the Adapter Options appears to be stubbed to support it if it was passed in:
4b83fa835c/packages/better-auth/src/adapters/kysely-adapter/kysely-adapter.ts (L12-L42)

4b83fa835c/packages/better-auth/src/adapters/kysely-adapter/kysely-adapter.ts (L374-L385)

This also impacts getMigrations() since that uses the createKyselyAdapter():
4b83fa835c/packages/better-auth/src/db/get-migration.ts (L85-L103)

What version of Better Auth are you using?

1.3.11

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:29 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6000",
    "release": "24.6.0",
    "cpuCount": 10,
    "cpuModel": "Apple M1 Max",
    "totalMemory": "64.00 GB",
    "freeMemory": "0.66 GB"
  },
  "node": {
    "version": "v22.18.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "10.9.3"
  },
  "frameworks": [
    {
      "name": "vue",
      "version": "^3.5.21"
    },
    {
      "name": "nuxt",
      "version": "^4.1.1"
    }
  ],
  "databases": [
    {
      "name": "better-sqlite3",
      "version": "^12.2.0"
    },
    {
      "name": "kysely",
      "version": "^0.28.7"
    }
  ],
  "betterAuth": {
    "version": "^1.3.11",
    "config": null
  }
}

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

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
import { Kysely } from "kysely"
import { D1Dialect } from "kysely-d1"

import type { D1Database } from "@cloudflare/workers-types"
import type { H3Event } from "h3"

export function createAuth(event: H3Event) {
  const cf = event.context.cloudflare
  const db = cf?.env?.APP_DB as D1Database | undefined

  // Create Kysely instance with D1 dialect 
  const dialect = new D1Dialect({ database: db })
  const kysely = new Kysely<any>({ dialect })

  return betterAuth({
    baseURL,
    secret: (process.env.BETTER_AUTH_SECRET || cf?.env?.BETTER_AUTH_SECRET) as string,
    // database: adapter,
    database: {
      db: kysely,
      type: "sqlite", // Required for proper detection
      transaction: false, // the db/utils.ts getAdapter() uses `createKyselyAdapter()` which ignores properties other than db and type! 
    } as any,

    // User schema with custom fields
    user: {
      additionalFields: {
        user_type: {
          type: "string",
          required: false,
          defaultValue: "adult" // Default to adult for parent/leader registrations
        }
      }
    },

    // Email/password authentication
    emailAndPassword: {
      enabled: true,
      requireEmailVerification: false // Can enable later
    },

    // OAuth providers (to be configured with env vars)
    socialProviders: {
      google: cf.env.GOOGLE_CLIENT_ID && cf.env.GOOGLE_CLIENT_SECRET ? {
        clientId: cf.env.GOOGLE_CLIENT_ID as string,
        clientSecret: cf.env.GOOGLE_CLIENT_SECRET as string,
        prompt: "select_account"
      } : undefined,
    },
  })

Additional context

Seems to be related to:

And it looks like @himself65 is working on an unrelated item also impacted by the introduction of transactions with:

Originally created by @joshualyon on GitHub (Sep 17, 2025). Originally assigned to: @himself65 on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce * Use `better-auth` 1.3.10 or newer * Use the `kysley-d1` (or `@atinux/kysely-d1`) `D1Dialect` * Attempt creating a new user with a social login ### Current vs. Expected behavior After getting redirected from the social login back to the better-auth `/api/auth/callback/{provider}` endpoint, when better-auth attempts to create a new user with a social login with the `D1Dialect`, it results in a failure of `unable_to_create_user` Social logins should continue to work in 1.3.10+ and newer even if this means explicitly passing in a `transaction: false` or similar to the `database` options configuration. Unfortunately, the internal `createKyselyAdapter(config)` that is called only returns the `kysely` and `databaseType` configuration options, so if you've explicitly passed `transaction: false`, it doesn't actually get passed forward to the final created better-auth adapter. https://github.com/better-auth/better-auth/blob/4b83fa835c2ed5efb62b8122ac1c11a415540ba0/packages/better-auth/src/adapters/kysely-adapter/dialect.ts#L53-L75 So when the `getAdapter()` is called in `db/utils.ts` it calls `createKyselyAdapter()` and effectively drops all the database configuration *except* for `db`, `type`, and `debugLogs`: https://github.com/better-auth/better-auth/blob/4b83fa835c2ed5efb62b8122ac1c11a415540ba0/packages/better-auth/src/db/utils.ts#L9-L36 **Critically, the `transaction` option is not passed forward to the `kysleyAdapter()` creation call** even though the Adapter Options appears to be stubbed to support it if it *was* passed in: https://github.com/better-auth/better-auth/blob/4b83fa835c2ed5efb62b8122ac1c11a415540ba0/packages/better-auth/src/adapters/kysely-adapter/kysely-adapter.ts#L12-L42 https://github.com/better-auth/better-auth/blob/4b83fa835c2ed5efb62b8122ac1c11a415540ba0/packages/better-auth/src/adapters/kysely-adapter/kysely-adapter.ts#L374-L385 This also impacts `getMigrations()` since that uses the `createKyselyAdapter()`: https://github.com/better-auth/better-auth/blob/4b83fa835c2ed5efb62b8122ac1c11a415540ba0/packages/better-auth/src/db/get-migration.ts#L85-L103 ### What version of Better Auth are you using? 1.3.11 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:29 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6000", "release": "24.6.0", "cpuCount": 10, "cpuModel": "Apple M1 Max", "totalMemory": "64.00 GB", "freeMemory": "0.66 GB" }, "node": { "version": "v22.18.0", "env": "development" }, "packageManager": { "name": "npm", "version": "10.9.3" }, "frameworks": [ { "name": "vue", "version": "^3.5.21" }, { "name": "nuxt", "version": "^4.1.1" } ], "databases": [ { "name": "better-sqlite3", "version": "^12.2.0" }, { "name": "kysely", "version": "^0.28.7" } ], "betterAuth": { "version": "^1.3.11", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" import { Kysely } from "kysely" import { D1Dialect } from "kysely-d1" import type { D1Database } from "@cloudflare/workers-types" import type { H3Event } from "h3" export function createAuth(event: H3Event) { const cf = event.context.cloudflare const db = cf?.env?.APP_DB as D1Database | undefined // Create Kysely instance with D1 dialect const dialect = new D1Dialect({ database: db }) const kysely = new Kysely<any>({ dialect }) return betterAuth({ baseURL, secret: (process.env.BETTER_AUTH_SECRET || cf?.env?.BETTER_AUTH_SECRET) as string, // database: adapter, database: { db: kysely, type: "sqlite", // Required for proper detection transaction: false, // the db/utils.ts getAdapter() uses `createKyselyAdapter()` which ignores properties other than db and type! } as any, // User schema with custom fields user: { additionalFields: { user_type: { type: "string", required: false, defaultValue: "adult" // Default to adult for parent/leader registrations } } }, // Email/password authentication emailAndPassword: { enabled: true, requireEmailVerification: false // Can enable later }, // OAuth providers (to be configured with env vars) socialProviders: { google: cf.env.GOOGLE_CLIENT_ID && cf.env.GOOGLE_CLIENT_SECRET ? { clientId: cf.env.GOOGLE_CLIENT_ID as string, clientSecret: cf.env.GOOGLE_CLIENT_SECRET as string, prompt: "select_account" } : undefined, }, }) ``` ### Additional context Seems to be related to: - #4414 - #4711 And it looks like @himself65 is working on an unrelated item also impacted by the introduction of transactions with: - #4718
GiteaMirror added the adapter label 2026-03-13 09:15:37 -05:00
Author
Owner

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

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

The root cause is that the transaction option from your database config isn't forwarded to the Kysely adapter—only db, type, and debugLogs are passed through, so transaction: false is ignored. As a result, Better Auth 1.3.10+ always wraps Kysely operations in a transaction by default, which breaks Cloudflare D1 since it doesn't support transactions. This also affects migrations, since the same adapter creation logic is used.

The relevant code paths are:

Workaround:
Until this is fixed upstream, the only reliable workaround is to fork Better Auth and patch getAdapter to forward the transaction option (and any other relevant options) from your config to the Kysely adapter. For example, in db/utils.ts, change the call to kyselyAdapter to include transaction: options.database.transaction if present.

Risks:
Disabling transactions globally for all Kysely adapters is not recommended unless you are only targeting D1, since other databases benefit from transactional safety. For D1, disabling transactions is required for compatibility.

For maintainers:
The fix is to ensure all relevant adapter options—including transaction—are forwarded from db/utils.ts to the adapter layer. This will restore expected behavior and allow D1 users to opt out of transactions as documented.

Let me know if you want a code snippet for the patch or have questions about a temporary override.

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Sep 17, 2025): <!-- Greeting --> Hi @joshualyon! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The root cause is that the `transaction` option from your `database` config isn't forwarded to the Kysely adapter—only `db`, `type`, and `debugLogs` are passed through, so `transaction: false` is ignored. As a result, Better Auth 1.3.10+ always wraps Kysely operations in a transaction by default, which breaks Cloudflare D1 since it doesn't support transactions. This also affects migrations, since the same adapter creation logic is used. The relevant code paths are: - [`getAdapter` in `db/utils.ts`](https://github.com/better-auth/better-auth/blob/d1fe10962079c82920ea078a9847f65a59907b81/packages/better-auth/src/db/utils.ts) only passes a subset of options to the adapter. - [`kyselyAdapter` supports a `transaction` option](https://github.com/better-auth/better-auth/blob/d1fe10962079c82920ea078a9847f65a59907b81/packages/better-auth/src/adapters/kysely-adapter/kysely-adapter.ts), but never receives it unless the propagation is fixed. **Workaround:** Until this is fixed upstream, the only reliable workaround is to fork Better Auth and patch `getAdapter` to forward the `transaction` option (and any other relevant options) from your config to the Kysely adapter. For example, in `db/utils.ts`, change the call to `kyselyAdapter` to include `transaction: options.database.transaction` if present. **Risks:** Disabling transactions globally for all Kysely adapters is not recommended unless you are only targeting D1, since other databases benefit from transactional safety. For D1, disabling transactions is required for compatibility. **For maintainers:** The fix is to ensure all relevant adapter options—including `transaction`—are forwarded from `db/utils.ts` to the adapter layer. This will restore expected behavior and allow D1 users to opt out of transactions as documented. Let me know if you want a code snippet for the patch or have questions about a temporary override. <!-- 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/90c922ba-d639-400b-9cfe-96b5fe96c8c2?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/90c922ba-d639-400b-9cfe-96b5fe96c8c2?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/90c922ba-d639-400b-9cfe-96b5fe96c8c2?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/90c922ba-d639-400b-9cfe-96b5fe96c8c2?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/90c922ba-d639-400b-9cfe-96b5fe96c8c2?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/90c922ba-d639-400b-9cfe-96b5fe96c8c2?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/90c922ba-d639-400b-9cfe-96b5fe96c8c2?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/4732)
Author
Owner

@himself65 commented on GitHub (Sep 17, 2025):

Hi, I'm checking this. Seems like there's some issues when bypass the transaction

@himself65 commented on GitHub (Sep 17, 2025): Hi, I'm checking this. Seems like there's some issues when bypass the transaction
Author
Owner

@himself65 commented on GitHub (Sep 17, 2025):

#4711 is not in the stable version yet. I will not cherry pick it until 1.4

@himself65 commented on GitHub (Sep 17, 2025): #4711 is not in the stable version yet. I will not cherry pick it until 1.4
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1953