[GH-ISSUE #969] Cloudflare: Error: The script will never generate a response. #8525

Closed
opened 2026-04-13 03:37:10 -05:00 by GiteaMirror · 37 comments
Owner

Originally created by @CMMA04 on GitHub (Dec 20, 2024).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/969

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Deploy or preview an aplication using Cloudlflare Pages or Workers, the issue does not show up if you use dev
  2. Try to sign in

Current vs. Expected behavior

Current:
Sign in, shows the following error in terminal:

✘ [ERROR] A hanging Promise was canceled. This happens when the worker runtime is waiting for a Promise from JavaScript to resolve, but has detected that the Promise cannot possibly ever resolve because all code and events related to the Promise's I/O context have already finished.


[wrangler:inf] POST /api/auth/sign-in/email 500 Internal Server Error (296ms)
✘ [ERROR] Uncaught (in response) Error: The script will never generate a response.

If you try again, it works.
It does not get catch by a try statement, however if you read the error value using:

const { data, error } = await authClient.signIn.email({
      email: event.data.email,
      password: event.data.password,
});
if (error) {
      console.log(error)
} 

It will say: undefined

Expected:
Sign in without issues

What version of Better Auth are you using?

1.0.22

Provide environment information

- OS: MacOS y Windows (tested using preview) and Cloudflare Pages
- Browser: Any
- Framework: Nuxt

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

Backend

Auth config (if applicable)

export const auth = betterAuth({
  appName: "test",
  database: drizzleAdapter(db, {
    provider: "pg",
    schema: {
      ...schema,
    },
  }),
  secondaryStorage: {
    get: async (key) => await redis.get(key),
    set: async (key, value, ttl) => {
      if (ttl) await redis.set(key, JSON.stringify(value), { ex: ttl });
      else await redis.set(key, JSON.stringify(value));
    },
    delete: async (key) => {
      await redis.del(key);
    },
  },
  plugins: [
    openAPI(),
  ],
  emailAndPassword: {
    enabled: true,
  },
  emailVerification: {
    sendVerificationEmail: async ({ user, url, token }) => {
      const resend = new Resend(process.env.RESEND_API_KEY);
      const data = await resend.emails.send({
        from: "noreply@test.com",
        to: user.email,
        subject: "Email verification",
        html: `Confirmation link: ${url}`,
      });
    },
  },
});

Additional context

For some reason it does not happen if you run under nuxt dev, so it is a Clouflare only issue

I apologize if it is a issue in our side or a known issue, I am learning to use this library and i am still new to auth enviorement

Originally created by @CMMA04 on GitHub (Dec 20, 2024). Original GitHub issue: https://github.com/better-auth/better-auth/issues/969 ### Is this suited for github? - [X] Yes, this is suited for github ### To Reproduce 1. Deploy or preview an aplication using Cloudlflare Pages or Workers, the issue does not show up if you use dev 2. Try to sign in ### Current vs. Expected behavior Current: Sign in, shows the following error in terminal: ``` ✘ [ERROR] A hanging Promise was canceled. This happens when the worker runtime is waiting for a Promise from JavaScript to resolve, but has detected that the Promise cannot possibly ever resolve because all code and events related to the Promise's I/O context have already finished. [wrangler:inf] POST /api/auth/sign-in/email 500 Internal Server Error (296ms) ✘ [ERROR] Uncaught (in response) Error: The script will never generate a response. ``` If you try again, it works. It does not get catch by a try statement, however if you read the error value using: ``` const { data, error } = await authClient.signIn.email({ email: event.data.email, password: event.data.password, }); if (error) { console.log(error) } ``` It will say: undefined Expected: Sign in without issues ### What version of Better Auth are you using? 1.0.22 ### Provide environment information ```bash - OS: MacOS y Windows (tested using preview) and Cloudflare Pages - Browser: Any - Framework: Nuxt ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript export const auth = betterAuth({ appName: "test", database: drizzleAdapter(db, { provider: "pg", schema: { ...schema, }, }), secondaryStorage: { get: async (key) => await redis.get(key), set: async (key, value, ttl) => { if (ttl) await redis.set(key, JSON.stringify(value), { ex: ttl }); else await redis.set(key, JSON.stringify(value)); }, delete: async (key) => { await redis.del(key); }, }, plugins: [ openAPI(), ], emailAndPassword: { enabled: true, }, emailVerification: { sendVerificationEmail: async ({ user, url, token }) => { const resend = new Resend(process.env.RESEND_API_KEY); const data = await resend.emails.send({ from: "noreply@test.com", to: user.email, subject: "Email verification", html: `Confirmation link: ${url}`, }); }, }, }); ``` ### Additional context For some reason it does not happen if you run under nuxt dev, so it is a Clouflare only issue I apologize if it is a issue in our side or a known issue, I am learning to use this library and i am still new to auth enviorement
GiteaMirror added the lockedbug labels 2026-04-13 03:37:11 -05:00
Author
Owner

@Bekacru commented on GitHub (Dec 21, 2024):

Cloudflare's CPU time isn't sufficient to hash and verify passwords, so email/password login doesn't work well in a worker environment. If email and pass is really needed, you can pass a custom hashing function that's less CPU-intensive (although not entirely secure). But, it's generally better to use social login, email OTP, magic links, and similar methods.

<!-- gh-comment-id:2558043662 --> @Bekacru commented on GitHub (Dec 21, 2024): Cloudflare's CPU time isn't sufficient to hash and verify passwords, so email/password login doesn't work well in a worker environment. If email and pass is really needed, you can pass a custom hashing function that's less CPU-intensive (although not entirely secure). But, it's generally better to use social login, email OTP, magic links, and similar methods.
Author
Owner

@zawasp commented on GitHub (Dec 27, 2024):

I have the same issue, but I doubt the CPU time is the problem as I'm on a paid plan. I also used Scrypt with Lucia and I didn't get these errors. Just for testing, I changed the hashing algo to a very simple one.
It also happens randomly on api/auth/get-session as well.
LE: it happens randomly on all better-auth endpoints, including api/auth/sign-out.

It could be something related to this issue
https://github.com/cloudflare/workerd/issues/210

<!-- gh-comment-id:2564073960 --> @zawasp commented on GitHub (Dec 27, 2024): I have the same issue, but I doubt the CPU time is the problem as I'm on a paid plan. I also used Scrypt with Lucia and I didn't get these errors. Just for testing, I changed the hashing algo to a very simple one. It also happens randomly on `api/auth/get-session` as well. LE: it happens randomly on all better-auth endpoints, including `api/auth/sign-out`. It could be something related to this issue https://github.com/cloudflare/workerd/issues/210
Author
Owner

@bjornpagen commented on GitHub (Feb 8, 2025):

experiencing this issue as well with magic links

export const auth = betterAuth({
	database: drizzleAdapter(db, {
		provider: "pg",
		schema
	}),
	advanced: {
		generateId: false
	},
	user: {
		additionalFields: {
			currentLanguageId: {
				type: "string",
				required: true,
				defaultValue: "en"
			}
		}
	},
	emailAndPassword: {
		enabled: false
	},
	plugins: [
		expo(),
		magicLink({
			sendMagicLink: async ({ email, url }) => {
				await postmark.sendEmail({
					From: "support@bjornpagen.com",
					To: email,
					Subject: "Sign in to Your App",
					HtmlBody: `<p>Click <a href="${url}">here</a> to sign in to your account.</p>`,
					TextBody: `Click the following link to sign in: ${url}`,
					MessageStream: "outbound"
				})
			}
		})
	],
	trustedOrigins: ["myapp://"]
})
<!-- gh-comment-id:2645930435 --> @bjornpagen commented on GitHub (Feb 8, 2025): experiencing this issue as well with magic links ``` export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", schema }), advanced: { generateId: false }, user: { additionalFields: { currentLanguageId: { type: "string", required: true, defaultValue: "en" } } }, emailAndPassword: { enabled: false }, plugins: [ expo(), magicLink({ sendMagicLink: async ({ email, url }) => { await postmark.sendEmail({ From: "support@bjornpagen.com", To: email, Subject: "Sign in to Your App", HtmlBody: `<p>Click <a href="${url}">here</a> to sign in to your account.</p>`, TextBody: `Click the following link to sign in: ${url}`, MessageStream: "outbound" }) } }) ], trustedOrigins: ["myapp://"] }) ```
Author
Owner

@bjornpagen commented on GitHub (Feb 8, 2025):

I think it has to do with the $context: Promise<AuthContext> field in the Auth type in auth.ts. I made a patch to try to get it to run on Cloudflare to test my hypothesis, but have been unable to build the betterauth package to test.

Related to this issue: https://news.ycombinator.com/item?id=34989560

My commit: -> 9137689ead

<!-- gh-comment-id:2645969259 --> @bjornpagen commented on GitHub (Feb 8, 2025): I think it has to do with the `$context: Promise<AuthContext>` field in the `Auth` type in `auth.ts`. I made a patch to try to get it to run on Cloudflare to test my hypothesis, but have been unable to build the betterauth package to test. Related to this issue: https://news.ycombinator.com/item?id=34989560 My commit: -> https://github.com/better-auth/better-auth/commit/9137689ead001a753ed70843a9aa875dcc80a9f5
Author
Owner

@saturnonearth commented on GitHub (Feb 12, 2025):

I think it has to do with the $context: Promise<AuthContext> field in the Auth type in auth.ts. I made a patch to try to get it to run on Cloudflare to test my hypothesis, but have been unable to build the betterauth package to test.

Related to this issue: https://news.ycombinator.com/item?id=34989560

My commit: -> 9137689

I have been experiencing similar issues to this with receiving errors such as The script will never generate a response and

`[Better Auth]:\u001b[0m INTERNAL_SERVER_ERROR

Error: Cannot perform I/O on behalf of a different request. I/O objects (such as streams, request/response bodies, and others) created in the context of one request handler cannot be accessed from a different request's handler. This is a limitation of Cloudflare Workers which allows us to improve overall performance. (I/O type: Writable)`

<!-- gh-comment-id:2654828967 --> @saturnonearth commented on GitHub (Feb 12, 2025): > I think it has to do with the `$context: Promise<AuthContext>` field in the `Auth` type in `auth.ts`. I made a patch to try to get it to run on Cloudflare to test my hypothesis, but have been unable to build the betterauth package to test. > > Related to this issue: https://news.ycombinator.com/item?id=34989560 > > My commit: -> [9137689](https://github.com/better-auth/better-auth/commit/9137689ead001a753ed70843a9aa875dcc80a9f5) I have been experiencing similar issues to this with receiving errors such as `The script will never generate a response` and `[Better Auth]:\u001b[0m INTERNAL_SERVER_ERROR Error: Cannot perform I/O on behalf of a different request. I/O objects (such as streams, request/response bodies, and others) created in the context of one request handler cannot be accessed from a different request's handler. This is a limitation of Cloudflare Workers which allows us to improve overall performance. (I/O type: Writable)`
Author
Owner

@ymansurozer commented on GitHub (Mar 1, 2025):

I have the exact same issue. All better-auth endpoints fail randomly (usually works 50% of the time and the rest gives the The script will never generate a response error).

I tried to give a custom hash and verify function, but nothing changes.

<!-- gh-comment-id:2692362317 --> @ymansurozer commented on GitHub (Mar 1, 2025): I have the exact same issue. All better-auth endpoints fail randomly (usually works 50% of the time and the rest gives the `The script will never generate a response` error). I tried to give a custom hash and verify function, but nothing changes.
Author
Owner

@saturnonearth commented on GitHub (Mar 1, 2025):

For me, this was fixed when I switched to using Neon DB, possibly because it uses a different connection style?

<!-- gh-comment-id:2692365985 --> @saturnonearth commented on GitHub (Mar 1, 2025): For me, this was fixed when I switched to using Neon DB, possibly because it uses a different connection style?
Author
Owner

@JinNgVN commented on GitHub (Mar 21, 2025):

I also got the error: The script will never generate a response. Any update on this?

<!-- gh-comment-id:2742407521 --> @JinNgVN commented on GitHub (Mar 21, 2025): I also got the error: `The script will never generate a response`. Any update on this?
Author
Owner

@Maxservais commented on GitHub (Apr 19, 2025):

I'm experiencing a similar issue with better-auth on Cloudflare Workers using Hono with the Drizzle adapter with D1.

Sign-in requests (POST /api/auth/sign-in/email) consistently take ~4.5-5 seconds.

Cloudflare Worker logs show high CPU time (~4.5s) nearly matching wall time (~4.7s), while D1 query latencies are very low (<10ms).

This seems to strongly support @Bekacru's comment regarding the default password hashing algorithm (scrypt) exceeding standard Worker CPU time limits during verification.

Not too sure what to do about it besides using social logins or using a custom hashing function..

<!-- gh-comment-id:2816661878 --> @Maxservais commented on GitHub (Apr 19, 2025): I'm experiencing a similar issue with `better-auth` on Cloudflare Workers using Hono with the Drizzle adapter with D1. Sign-in requests (`POST /api/auth/sign-in/email`) consistently take ~4.5-5 seconds. Cloudflare Worker logs show high CPU time (~4.5s) nearly matching wall time (~4.7s), while D1 query latencies are very low (<10ms). This seems to strongly support @Bekacru's comment regarding the default password hashing algorithm (scrypt) exceeding standard Worker CPU time limits during verification. Not too sure what to do about it besides using social logins or using a custom hashing function..
Author
Owner

@ariburaco commented on GitHub (Apr 19, 2025):

Literally have the same issue, I've just upgrade my plan to see CPU times, some requests are taking around 3-4 seconds, using hono, turso db..

I didn't see any issue with google social login at the moment.. I am adapting to CF workers, hope we can fix this issue to support CF Workers in the free tier..

Image
<!-- gh-comment-id:2816667380 --> @ariburaco commented on GitHub (Apr 19, 2025): Literally have the same issue, I've just upgrade my plan to see CPU times, some requests are taking around 3-4 seconds, using hono, turso db.. I didn't see any issue with google social login at the moment.. I am adapting to CF workers, hope we can fix this issue to support CF Workers in the free tier.. <img width="1498" alt="Image" src="https://github.com/user-attachments/assets/9b7a83ff-21a3-4e4f-b6ab-bd5ee250b06c" />
Author
Owner

@Maxservais commented on GitHub (Apr 19, 2025):

Literally have the same issue, I've just upgrade my plan to see CPU times, some requests are taking around 3-4 seconds, using hono, turso db..

I didn't see any issue with google social login at the moment.. I am adapting to CF workers, hope we can fix this issue to support CF Workers in the free tier..
Image

How did you manage to get CPU time down to 200ms? This is an example of a signup for me on CF Workers paid plan - don't get why it's so much slower than the requests in your screenshot

Image

<!-- gh-comment-id:2816669051 --> @Maxservais commented on GitHub (Apr 19, 2025): > Literally have the same issue, I've just upgrade my plan to see CPU times, some requests are taking around 3-4 seconds, using hono, turso db.. > > I didn't see any issue with google social login at the moment.. I am adapting to CF workers, hope we can fix this issue to support CF Workers in the free tier.. > <img alt="Image" width="1498" src="https://private-user-images.githubusercontent.com/34109588/435388515-9b7a83ff-21a3-4e4f-b6ab-bd5ee250b06c.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDUwNjI3MDksIm5iZiI6MTc0NTA2MjQwOSwicGF0aCI6Ii8zNDEwOTU4OC80MzUzODg1MTUtOWI3YTgzZmYtMjFhMy00ZTRmLWI2YWItYmQ1ZWUyNTBiMDZjLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA0MTklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNDE5VDExMzMyOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWMzMzhhYmQ3ZTY1N2FkNTMzMTliMGRiZWMwNDk2MzhmMzRiZmQwYzU2MmFjZmE1OGMyNjBjYTQzNjE0ZmE4MTAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.F7HlAzF2j499_y2rS5U1O_3jLOYHfMZU3qTDish3z1Q"> How did you manage to get CPU time down to 200ms? This is an example of a signup for me on CF Workers paid plan - don't get why it's so much slower than the requests in your screenshot ![Image](https://github.com/user-attachments/assets/a8323d66-3aa6-4fa5-b53d-b60a320bd07f)
Author
Owner

@ariburaco commented on GitHub (Apr 19, 2025):

Hey! 👋

Just wanted to share what I’ve done on my side in case it helps clarify any performance differences or implementation questions.

I’m using Hono with Cloudflare Workers and integrating better-auth for authentication. The backend uses Turso (libSQL) via @libsql/client/web, wrapped with drizzle-orm for DB access and schema.

Breakdown of the setup:
• getDb(): A helper function to initialize a Turso DB client using environment variables (TURSO_CONNECTION_URL, TURSO_AUTH_TOKEN). This returns a drizzle-wrapped client, ready to use with schema and relations.
• authOptions + createAuth(): Configures better-auth with drizzleAdapter for SQLite, allowing us to pass the schema and hook up authentication to the DB.
• In the Hono app middleware, I:
• Instantiate the DB and auth client per request.
• Bind them to the context (c.set('db', ...) and c.set('auth', ...)) so route handlers can access them easily.
• Close the DB connection after each request to avoid issues with Cloudflare’s worker runtime and resource limits.
• The route app.on(['/POST', 'GET'], '/api/auth/*', ...) just delegates requests to the Better Auth handler, with improved error handling/logging.

using "better-auth": "^1.2.7"

Btw, sometimes it goes to 3 to 4 seconds, not always around ~200ms.. and the lower once just already have a user requests..

signups:
Image

signins:

Image
import { Hono } from 'hono';
import { betterAuth } from "better-auth";
import { createClient as createClientEdge } from "@libsql/client/web";
import { drizzle } from "drizzle-orm/libsql";
import { drizzleAdapter } from "better-auth/adapters/drizzle";

// These are fron the monorepo packages
// get the db util
function getDb(dbUrl?: string, authToken?: string) {
  const url = dbUrl ?? process.env.TURSO_CONNECTION_URL ?? "";
  const token = authToken ?? process.env.TURSO_AUTH_TOKEN ?? "";

  const turso = createClientEdge({
    url: url,
    authToken: token,
  });

  return drizzle(turso, { schema: { ...schema, ...relations } });
}

// get the auth options
export const authOptions = (db?: DB): BetterAuthOptions => ({
  appName: "App Name",
  database: drizzleAdapter(db ?? getDb(), {
    provider: "sqlite",
    schema: schema, // pass the schema
  }),
  emailAndPassword: {
    enabled: true,
  },
  account: {
    accountLinking: {
      enabled: true,
    },
  },
  socialProviders: {
    google: {}, // pass the google options,
  },
  session: {
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60, // Cache duration in seconds
    },
  },
  trustedOrigins: [
    "http://localhost:3000",
   ,
  ],
  advanced: {
    crossSubDomainCookies: {
      enabled: true,
    },
    defaultCookieAttributes: {
      secure: true,
      httpOnly: true,
      sameSite: "none", // Allows CORS-based cookie sharing across subdomains
      partitioned: true, // New browser standards will mandate this for foreign cookies
    },
  },
}

export const createAuth = (db?: DB) => {
  return betterAuth(authOptions(db));
};


// The actual hono/cloudflare App
interface CloudflareBindings {
  ...envBindings
}

type Variables = {
  db: DB;
  auth: Auth;
};

export type HonoContext = {
  Bindings: CloudflareBindings;
  Variables: Variables;
};

const app = new Hono<HonoContext>();

// Bind the db and auth to the context so, each request can access them 
app.use('*', async (c, next) => {
  const db = getDb(c.env.TURSO_CONNECTION_URL, c.env.TURSO_AUTH_TOKEN);
  c.set('db', db);
  const auth = createAuth(db);
  c.set('auth', auth);

  try {
    await next();
  } finally {
    // close the db connection after the request is done to handle worker errors!
    db.$client.close();
  }
});

// Mount Better Auth handler with improved error handling
app.on(['POST', 'GET'], '/api/auth/*', async (c) => {
  const path = c.req.path;
  const method = c.req.method;

  try {
    return c.var.auth.handler(c.req.raw);
  } catch (error) {
    console.error(`Auth handler error for ${path} - ${method}:`, error);
    return c.json(
      {
        status: 'error',
        message: 'Failed to process authentication request',
        error: error instanceof Error ? error.message : String(error),
      },
      500
    );
  }
});

const handler: ExportedHandler<CloudflareBindings> = {
  fetch: app.fetch,
};

export default handler;


<!-- gh-comment-id:2816679156 --> @ariburaco commented on GitHub (Apr 19, 2025): Hey! 👋 Just wanted to share what I’ve done on my side in case it helps clarify any performance differences or implementation questions. I’m using Hono with Cloudflare Workers and integrating better-auth for authentication. The backend uses Turso (libSQL) via @libsql/client/web, wrapped with drizzle-orm for DB access and schema. Breakdown of the setup: • getDb(): A helper function to initialize a Turso DB client using environment variables (TURSO_CONNECTION_URL, TURSO_AUTH_TOKEN). This returns a drizzle-wrapped client, ready to use with schema and relations. • authOptions + createAuth(): Configures better-auth with drizzleAdapter for SQLite, allowing us to pass the schema and hook up authentication to the DB. • In the Hono app middleware, I: • Instantiate the DB and auth client per request. • Bind them to the context (c.set('db', ...) and c.set('auth', ...)) so route handlers can access them easily. • Close the DB connection after each request to avoid issues with Cloudflare’s worker runtime and resource limits. • The route app.on(['/POST', 'GET'], '/api/auth/*', ...) just delegates requests to the Better Auth handler, with improved error handling/logging. using `"better-auth": "^1.2.7"` Btw, sometimes it goes to 3 to 4 seconds, not always around ~200ms.. and the lower once just already have a user requests.. signups: <img width="376" alt="Image" src="https://github.com/user-attachments/assets/3f52b043-5708-49f0-b54d-6f6dc127b293" /> signins: <img width="1460" alt="Image" src="https://github.com/user-attachments/assets/40c58618-1bf3-49ff-acc9-5ee1f929b6fe" /> ```ts import { Hono } from 'hono'; import { betterAuth } from "better-auth"; import { createClient as createClientEdge } from "@libsql/client/web"; import { drizzle } from "drizzle-orm/libsql"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; // These are fron the monorepo packages // get the db util function getDb(dbUrl?: string, authToken?: string) { const url = dbUrl ?? process.env.TURSO_CONNECTION_URL ?? ""; const token = authToken ?? process.env.TURSO_AUTH_TOKEN ?? ""; const turso = createClientEdge({ url: url, authToken: token, }); return drizzle(turso, { schema: { ...schema, ...relations } }); } // get the auth options export const authOptions = (db?: DB): BetterAuthOptions => ({ appName: "App Name", database: drizzleAdapter(db ?? getDb(), { provider: "sqlite", schema: schema, // pass the schema }), emailAndPassword: { enabled: true, }, account: { accountLinking: { enabled: true, }, }, socialProviders: { google: {}, // pass the google options, }, session: { cookieCache: { enabled: true, maxAge: 5 * 60, // Cache duration in seconds }, }, trustedOrigins: [ "http://localhost:3000", , ], advanced: { crossSubDomainCookies: { enabled: true, }, defaultCookieAttributes: { secure: true, httpOnly: true, sameSite: "none", // Allows CORS-based cookie sharing across subdomains partitioned: true, // New browser standards will mandate this for foreign cookies }, }, } export const createAuth = (db?: DB) => { return betterAuth(authOptions(db)); }; // The actual hono/cloudflare App interface CloudflareBindings { ...envBindings } type Variables = { db: DB; auth: Auth; }; export type HonoContext = { Bindings: CloudflareBindings; Variables: Variables; }; const app = new Hono<HonoContext>(); // Bind the db and auth to the context so, each request can access them app.use('*', async (c, next) => { const db = getDb(c.env.TURSO_CONNECTION_URL, c.env.TURSO_AUTH_TOKEN); c.set('db', db); const auth = createAuth(db); c.set('auth', auth); try { await next(); } finally { // close the db connection after the request is done to handle worker errors! db.$client.close(); } }); // Mount Better Auth handler with improved error handling app.on(['POST', 'GET'], '/api/auth/*', async (c) => { const path = c.req.path; const method = c.req.method; try { return c.var.auth.handler(c.req.raw); } catch (error) { console.error(`Auth handler error for ${path} - ${method}:`, error); return c.json( { status: 'error', message: 'Failed to process authentication request', error: error instanceof Error ? error.message : String(error), }, 500 ); } }); const handler: ExportedHandler<CloudflareBindings> = { fetch: app.fetch, }; export default handler; ```
Author
Owner

@ShubhamVsCode commented on GitHub (Apr 20, 2025):

yes, i have been facing this same issue so i have to initialize the db and auth inside the request handle to work properly in cloudflare environment, i have implemented Auth and DB class so that when we run better-auth commands like npx @better-auth/cli generate, we can just export the auth like

export const auth = new Auth(new DB())
<!-- gh-comment-id:2817274225 --> @ShubhamVsCode commented on GitHub (Apr 20, 2025): yes, i have been facing this same issue so i have to initialize the db and auth inside the request handle to work properly in cloudflare environment, i have implemented `Auth` and `DB` class so that when we run `better-auth` commands like `npx @better-auth/cli generate`, we can just export the auth like ```ts export const auth = new Auth(new DB()) ```
Author
Owner

@kziemski commented on GitHub (Apr 21, 2025):

has anyone tried explicitly using subtlecrypto in the hashing and verification? would there be any performance improvement there?

<!-- gh-comment-id:2819446748 --> @kziemski commented on GitHub (Apr 21, 2025): has anyone tried explicitly using [subtlecrypto](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#subtlecrypto-methods) in the hashing and verification? would there be any performance improvement there?
Author
Owner

@tobimori commented on GitHub (Apr 23, 2025):

BetterAuth ships with a pure JS implementation (@noble/hashes) by default. However, since last year, Cloudflare supports native Scrypt hashing using node:crypto compat layer: https://developers.cloudflare.com/workers/platform/changelog/#2024-07-03

Replace the custom hashing methods with that and your 5s+ loads should go away.

@kziemski SubtleCrypto might not be the best choice for password hashing. You could try PBKDF2 with a maximum of 100,000 iterations (which is the CF limit). However, just so you know, OWASP suggests using 600,000 iterations for PBKDF2-HMAC-SHA256 and 210,000 for PBKDF2-HMAC-SHA512. So, it might not be ideal for password hashing these days.

<!-- gh-comment-id:2825167155 --> @tobimori commented on GitHub (Apr 23, 2025): BetterAuth ships with a pure JS implementation (@noble/hashes) by default. However, since last year, Cloudflare supports native Scrypt hashing using node:crypto compat layer: https://developers.cloudflare.com/workers/platform/changelog/#2024-07-03 Replace the custom hashing methods with that and your 5s+ loads should go away. @kziemski SubtleCrypto might not be the best choice for password hashing. You could try PBKDF2 with a maximum of 100,000 iterations (which is the CF limit). However, just so you know, OWASP suggests using 600,000 iterations for PBKDF2-HMAC-SHA256 and 210,000 for PBKDF2-HMAC-SHA512. So, it might not be ideal for password hashing these days.
Author
Owner

@kziemski commented on GitHub (Apr 23, 2025):

@tobimori you recommended Replace with noble/hashes or cf native?

<!-- gh-comment-id:2825181836 --> @kziemski commented on GitHub (Apr 23, 2025): @tobimori you recommended Replace with noble/hashes or cf native?
Author
Owner

@tobimori commented on GitHub (Apr 23, 2025):

@kziemski - replace it with scrypt from node:crypto and enable nodejs compat in wrangler.toml. @noble/hashes is the current implementation already used by Better Auth.

<!-- gh-comment-id:2825189189 --> @tobimori commented on GitHub (Apr 23, 2025): @kziemski - replace it with scrypt from node:crypto and enable nodejs compat in wrangler.toml. @noble/hashes is the current implementation already used by Better Auth.
Author
Owner

@bzuker commented on GitHub (Apr 27, 2025):

@tobimori I implemented this and it works correctly Cloudflare workers. Do you see any drawbacks with this implementation?

export const auth = lazy((env: Env) =>
  betterAuth({
    database: drizzleAdapter(drizzle(env.DB), {
      provider: 'sqlite',
      schema: schema,
      usePlural: true,
    }),
    emailAndPassword: {
      enabled: true,
      password: {
        hash: async (password) => {
          // use scrypt from node:crypto
          const salt = randomBytes(16).toString('hex')
          const hash = scryptSync(password, salt, 64).toString('hex')
          return `${salt}:${hash}`
        },
        verify: async ({ hash, password }) => {
          const [salt, key] = hash.split(':')
          const keyBuffer = Buffer.from(key, 'hex')
          const hashBuffer = scryptSync(password, salt, 64)
          return keyBuffer.equals(hashBuffer)
        },
      },
    },
  }),
)
<!-- gh-comment-id:2833532886 --> @bzuker commented on GitHub (Apr 27, 2025): @tobimori I implemented this and it works correctly Cloudflare workers. Do you see any drawbacks with this implementation? ```ts export const auth = lazy((env: Env) => betterAuth({ database: drizzleAdapter(drizzle(env.DB), { provider: 'sqlite', schema: schema, usePlural: true, }), emailAndPassword: { enabled: true, password: { hash: async (password) => { // use scrypt from node:crypto const salt = randomBytes(16).toString('hex') const hash = scryptSync(password, salt, 64).toString('hex') return `${salt}:${hash}` }, verify: async ({ hash, password }) => { const [salt, key] = hash.split(':') const keyBuffer = Buffer.from(key, 'hex') const hashBuffer = scryptSync(password, salt, 64) return keyBuffer.equals(hashBuffer) }, }, }, }), )
Author
Owner

@maxprilutskiy commented on GitHub (May 25, 2025):

If you're using Cloudflare + Remix (React Router), here's how you can get rid of the The script will never generate a response error.

TLDR: Instantiate your Prisma/etc + BetterAuth clients per-request, instead of doing that in file module scope.

If you have the following code to init your better auth:

// utils/auth.server.ts
export const serverAuth = betterAuth({
    database: prismaAdapter(db, {
      provider: "postgresql",
    }),
    plugins: [
      // ...
    ],
  });

then you need to move it to e.g. workers/auth.ts and replace with:

// workers/auth.ts
export function createAuth(db: PrismaClient) {
  return betterAuth({
    database: prismaAdapter(db, {
      provider: "postgresql",
    }),
    plugins: [
      // ...
    ],
  });
}

Then, in your Cloudflare worker entrypoint file (e.g. workers/app.ts), update your export default to have the auth/orm init code:

// workers/app.ts
export default {
  async fetch(request, env, ctx) {
    const db = createDb(env.DATABASE_URL); // <- create instances here
    const auth = createAuth(db); // <-

    return requestHandler(request, {
      auth, // <- then inject them into your app context here
      db, // and here
      cloudflare: { env, ctx },
    });
  },
} satisfies ExportedHandler<Env>;

Bonus: to make sure you get the type safety in your context, include auth and db into your AppLoadContext definition:

// This usually is in the Cloudflare's entrypoint file as well (e.g. workers/app.ts)
declare module "react-router" {
  export interface AppLoadContext {
    auth: ReturnType<typeof createAuth>; // <-
    db: ReturnType<typeof createDb>; // <-
    cloudflare: {
      env: Env;
      ctx: ExecutionContext;
    };
  }
}

Finally, update your /api/auth/* handlers to use auth instance from the context, instead of importing it from a module:

import type { ActionFunctionArgs, AppLoadContext } from "react-router";
import type { LoaderFunctionArgs } from "react-router";

export const loader = async (args: LoaderFunctionArgs<AppLoadContext>) => args.context.auth.handler(args.request);

export const action = async (args: ActionFunctionArgs<AppLoadContext>) => args.context.auth.handler(args.request);

Done.

Hope this helps!

<!-- gh-comment-id:2907514582 --> @maxprilutskiy commented on GitHub (May 25, 2025): If you're using Cloudflare + Remix (React Router), here's how you can get rid of the `The script will never generate a response` error. *TLDR*: Instantiate your Prisma/etc + BetterAuth clients per-request, instead of doing that in file module scope. If you have the following code to init your better auth: ```typescript // utils/auth.server.ts export const serverAuth = betterAuth({ database: prismaAdapter(db, { provider: "postgresql", }), plugins: [ // ... ], }); ``` then you need to move it to e.g. `workers/auth.ts` and replace with: ```typescript // workers/auth.ts export function createAuth(db: PrismaClient) { return betterAuth({ database: prismaAdapter(db, { provider: "postgresql", }), plugins: [ // ... ], }); } ``` Then, in your Cloudflare worker entrypoint file (e.g. `workers/app.ts`), update your `export default` to have the auth/orm init code: ```typescript // workers/app.ts export default { async fetch(request, env, ctx) { const db = createDb(env.DATABASE_URL); // <- create instances here const auth = createAuth(db); // <- return requestHandler(request, { auth, // <- then inject them into your app context here db, // and here cloudflare: { env, ctx }, }); }, } satisfies ExportedHandler<Env>; ``` Bonus: to make sure you get the type safety in your context, include `auth` and `db` into your `AppLoadContext` definition: ```typescript // This usually is in the Cloudflare's entrypoint file as well (e.g. workers/app.ts) declare module "react-router" { export interface AppLoadContext { auth: ReturnType<typeof createAuth>; // <- db: ReturnType<typeof createDb>; // <- cloudflare: { env: Env; ctx: ExecutionContext; }; } } ``` Finally, update your `/api/auth/*` handlers to use auth instance from the context, instead of importing it from a module: ```typescript import type { ActionFunctionArgs, AppLoadContext } from "react-router"; import type { LoaderFunctionArgs } from "react-router"; export const loader = async (args: LoaderFunctionArgs<AppLoadContext>) => args.context.auth.handler(args.request); export const action = async (args: ActionFunctionArgs<AppLoadContext>) => args.context.auth.handler(args.request); ``` Done. Hope this helps!
Author
Owner

@Gonzalo-Bruna commented on GitHub (Jun 1, 2025):

Hi! For anyone still having the issue, this is how I fixed it:

I have a Nitro backend with the same issue. All endpoints fail exactly half the times. One request is done correctly, and then the other one fails always.
I made several changes and ran multiple tests to pinpoint the issue, and here's what I found:

  • The root cause seems to be the usage of the pg library as better-auth's default, and it's causing an issue with Cloudflare Workers
  • I discovered this because I have a middleware in my app that retrieves the session on every request using the auth.api.getSession(); method, and then check the user's role to see if he has the correct permissions. I created two test routes, one with this middleware and one without the middleware, and the one with the middleware failed half the times, and the one without the middleware worked 100% of the times.

So, how did I fix it?

In the first place, since I am using postgres.js as my database client, I tried using this library to directly replace pg in the database configuration, like this:

import { PostgresJSDialect } from "kysely-postgres-js"

export function createAuth() {
    return BetterAuth({
        database: new PostgresJSDialect({
            postgres: postgres(process.env.SUPABASE_DB_URL as string),
        }),
    });
}

However, this didn't work because the kysely-postgres-js package seems to not be working, it was throwing an error. So I looked for another solution, and found NeonDB.
I didn't have to migrate my Supabase database to NeonDB, I just used the serverless package that comes with NeonDB, and a Kysely adapter, like this:

First install the packages:

pnpm add kysely-neon @neondatabase/serverless

Then updated the code:

import { NeonDialect } from "kysely-neon";

export function createAuth() {
    return BetterAuth({
        database: new NeonDialect({
            connectionString: process.env.SUPABASE_DB_URL,
        }),
    });
}

And after pushing the changes, it actually worked! Now, what's the issue with this solution? The kysely-neon package is quite outdated (latest commit 2 years ago), so it uses an old version of the neon db serverless package (0.4.3) when the current version is 1.0.0. This causes a peer-dependency error when installing, but at least using pnpm it still worked and solved the issue.
There is a pull-request in the repo opened a month ago updating the packages to the latest versions, however, since the repository isn't being actively maintained, it hasn't been merged, and I don't believe they plan to support this package at all. The only chance would be that somebody forks the repo and update the packages himself.

Hope this helps someone having the issue!

<!-- gh-comment-id:2926186624 --> @Gonzalo-Bruna commented on GitHub (Jun 1, 2025): Hi! For anyone still having the issue, this is how I fixed it: I have a Nitro backend with the same issue. All endpoints fail exactly half the times. One request is done correctly, and then the other one fails always. I made several changes and ran multiple tests to pinpoint the issue, and here's what I found: - The root cause seems to be the usage of the `pg` library as better-auth's default, and it's causing an issue with Cloudflare Workers - I discovered this because I have a middleware in my app that retrieves the session on every request using the `auth.api.getSession();` method, and then check the user's role to see if he has the correct permissions. I created two test routes, one with this middleware and one without the middleware, and the one with the middleware failed half the times, and the one without the middleware worked 100% of the times. So, how did I fix it? In the first place, since I am using `postgres.js` as my database client, I tried using this library to directly replace `pg` in the database configuration, like this: ```js import { PostgresJSDialect } from "kysely-postgres-js" export function createAuth() { return BetterAuth({ database: new PostgresJSDialect({ postgres: postgres(process.env.SUPABASE_DB_URL as string), }), }); } ``` However, this didn't work because the `kysely-postgres-js` package seems to not be working, it was throwing an error. So I looked for another solution, and found `NeonDB`. I didn't have to migrate my `Supabase` database to `NeonDB`, I just used the serverless package that comes with NeonDB, and a `Kysely` adapter, like this: First install the packages: ```bash pnpm add kysely-neon @neondatabase/serverless ``` Then updated the code: ```js import { NeonDialect } from "kysely-neon"; export function createAuth() { return BetterAuth({ database: new NeonDialect({ connectionString: process.env.SUPABASE_DB_URL, }), }); } ``` And after pushing the changes, it actually worked! Now, what's the issue with this solution? The [kysely-neon](https://github.com/seveibar/kysely-neon) package is quite outdated (latest commit 2 years ago), so it uses an old version of the neon db serverless package (0.4.3) when the current version is 1.0.0. This causes a peer-dependency error when installing, but at least using pnpm it still worked and solved the issue. There is a pull-request in the repo opened a month ago updating the packages to the latest versions, however, since the repository isn't being actively maintained, it hasn't been merged, and I don't believe they plan to support this package at all. The only chance would be that somebody forks the repo and update the packages himself. Hope this helps someone having the issue!
Author
Owner

@illbexyz commented on GitHub (Jun 2, 2025):

My setup is an expo-router application initialised as described in https://www.better-auth.com/docs/integrations/expo.

I'm having the same issue, except I'm getting this error on social sign-in (/api/auth/sign-in/social). The application works fine locally, but fails ~50% of the times when deployed over Expo EAS Hosting. On Vercel hosting I get a timeout instead.

Unfortunately none of the workarounds mentioned in this thread worked for me.

Here's my auth.ts for reference:

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg",
  }),
  plugins: [expo()],
  trustedOrigins: ["exp://"],
  socialProviders: {
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    },
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      prompt: "select_account",
      redirectURI: process.env.GOOGLE_REDIRECT_URI,
    },
  },
  user: {
    additionalFields: {
      role: {
        type: "string",
      },
    },
  }
});
<!-- gh-comment-id:2930785950 --> @illbexyz commented on GitHub (Jun 2, 2025): My setup is an `expo-router` application initialised as described in https://www.better-auth.com/docs/integrations/expo. I'm having the same issue, except I'm getting this error on social sign-in (`/api/auth/sign-in/social`). The application works fine locally, but fails ~50% of the times when deployed over Expo EAS Hosting. On Vercel hosting I get a timeout instead. Unfortunately none of the workarounds mentioned in this thread worked for me. Here's my `auth.ts` for reference: ```ts export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", }), plugins: [expo()], trustedOrigins: ["exp://"], socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID!, clientSecret: process.env.GITHUB_CLIENT_SECRET!, }, google: { clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET!, prompt: "select_account", redirectURI: process.env.GOOGLE_REDIRECT_URI, }, }, user: { additionalFields: { role: { type: "string", }, }, } }); ```
Author
Owner

@ebalo55 commented on GitHub (Jun 6, 2025):

With the latest version of wrangler this happens in dev command too, no matter if using social logins, passkeys, or passwords.

The solution provided here solved my problem with passwords, but I am unable to get it to work for socials and passkeys.

I'm using Prisma (with Xata), and even applying an adaptation of this strategy does not work.

Also, increasing wrangler CPU time is useless.

<!-- gh-comment-id:2947773344 --> @ebalo55 commented on GitHub (Jun 6, 2025): With the latest version of wrangler this happens in `dev` command too, no matter if using social logins, passkeys, or passwords. The solution provided [here](https://github.com/better-auth/better-auth/issues/969#issuecomment-2833532886) solved my problem with passwords, but I am unable to get it to work for socials and passkeys. I'm using Prisma (with Xata), and even applying an adaptation of [this strategy](https://github.com/better-auth/better-auth/issues/969#issuecomment-2907514582) does not work. Also, increasing wrangler CPU time is useless.
Author
Owner

@GuuuWei commented on GitHub (Jun 15, 2025):

"The script will never generate a response"
When I use supabase and Cloudflare, it seems that the pg library is not responding.
When I changed to Cloudflare D1, it worked fine.

<!-- gh-comment-id:2973515420 --> @GuuuWei commented on GitHub (Jun 15, 2025): "The script will never generate a response" When I use supabase and Cloudflare, it seems that the pg library is not responding. When I changed to Cloudflare D1, it worked fine.
Author
Owner

@Gonzalo-Bruna commented on GitHub (Jun 15, 2025):

After further investigation, I discovered that the root cause of the issue is actually the PG library. It seems that PG doesn’t work reliably in serverless environments — I suspect there’s a part of the library that isn’t properly awaiting a request, or perhaps there’s an incorrect use of better-auth somewhere.

How did I figure this out? Initially, I was using kysely-neon for the database connection. Since that package is quite outdated (it hasn’t had a commit in over two years), I decided to improve the setup by using the Pool class directly from the NeonDB serverless package instead of going through a Kysely adapter. However, when I made that change, the app started failing again on Cloudflare.

Why? Because under the hood, the NeonDB serverless library also relies on PG, leading to the same error we’ve all been seeing:
"The script will never generate a response."

So the solution is either:

  • Use a Kysely adapter with a database library that works well in serverless environments, or
  • Switch to a different type of database entirely.

In my case, I’m currently using the kysely adapter for NeonDB. Although it’s outdated and a bit slower than if it were actively maintained (because it uses the version 0.4.2 of the neon serverless driver instead of the latest 1.0.2), it still works just fine for my needs.

<!-- gh-comment-id:2973913995 --> @Gonzalo-Bruna commented on GitHub (Jun 15, 2025): After further investigation, I discovered that the root cause of the issue is actually the PG library. It seems that PG doesn’t work reliably in serverless environments — I suspect there’s a part of the library that isn’t properly awaiting a request, or perhaps there’s an incorrect use of better-auth somewhere. How did I figure this out? Initially, I was using kysely-neon for the database connection. Since that package is quite outdated (it hasn’t had a commit in over two years), I decided to improve the setup by using the Pool class directly from the NeonDB serverless package instead of going through a Kysely adapter. However, when I made that change, the app started failing again on Cloudflare. Why? Because under the hood, the NeonDB serverless library also relies on PG, leading to the same error we’ve all been seeing: "The script will never generate a response." So the solution is either: - Use a Kysely adapter with a database library that works well in serverless environments, or - Switch to a different type of database entirely. In my case, I’m currently using the kysely adapter for NeonDB. Although it’s outdated and a bit slower than if it were actively maintained (because it uses the version 0.4.2 of the neon serverless driver instead of the latest 1.0.2), it still works just fine for my needs.
Author
Owner

@Gonzalo-Bruna commented on GitHub (Jun 16, 2025):

@Bekacru Sorry might I ask why this was marked as "completed", when a lot of people is still experiencing the issue?

<!-- gh-comment-id:2974938551 --> @Gonzalo-Bruna commented on GitHub (Jun 16, 2025): @Bekacru Sorry might I ask why this was marked as "completed", when a lot of people is still experiencing the issue?
Author
Owner

@Bekacru commented on GitHub (Jun 16, 2025):

@Gonzalo-Bruna Most people who’ve run into this issue so far are using libraries that aren't suported by CF environments, likepg or similar libraries. So it’s not something we can fix on the Better Auth side. That said, I’ve added this to our issue tracker so we can improve the documentation around using Better Auth with Cloudflare.

But if you’ve had a different experience that points to a bug or limitation within Better Auth itself, please let me know - I’d be happy to re-open the issue and look into it.

<!-- gh-comment-id:2974961072 --> @Bekacru commented on GitHub (Jun 16, 2025): @Gonzalo-Bruna Most people who’ve run into this issue so far are using libraries that aren't suported by CF environments, like`pg` or similar libraries. So it’s not something we can fix on the Better Auth side. That said, I’ve added this to our issue tracker so we can improve the documentation around using Better Auth with Cloudflare. But if you’ve had a different experience that points to a bug or limitation within Better Auth itself, please let me know - I’d be happy to re-open the issue and look into it.
Author
Owner

@TinsFox commented on GitHub (Jun 22, 2025):

https://opennext.js.org/cloudflare/troubleshooting#error-cannot-perform-io-on-behalf-of-a-different-request

Maybe this can solve this problem.

<!-- gh-comment-id:2994247275 --> @TinsFox commented on GitHub (Jun 22, 2025): https://opennext.js.org/cloudflare/troubleshooting#error-cannot-perform-io-on-behalf-of-a-different-request Maybe this can solve this problem.
Author
Owner

@atsuki44 commented on GitHub (Jul 1, 2025):

I encountered the same CPU time limit issue when deploying better-auth with Hono on Cloudflare Workers.
I was able to resolve the timeout by using a custom hash function with Node.js crypto:

emailAndPassword: {
      enabled: true,
      password: {
        hash: async (password) => {
          const salt = crypto.getRandomValues(new Uint8Array(16));
          const saltHex = Array.from(salt).map(b => b.toString(16).padStart(2, '0')).join('');
          
          const key = scryptSync(
            password.normalize("NFKC"), 
            saltHex, 
            64,
            {
              N: 16384,
              r: 16,
              p: 1,
              maxmem: 128 * 16384 * 16 * 2 
            }
          );
          
          const keyHex = Array.from(key).map(b => b.toString(16).padStart(2, '0')).join('');
          return `${saltHex}:${keyHex}`;
        },
        verify: async ({ hash, password }) => {
          const [saltHex, keyHex] = hash.split(":");
          
          const targetKey = scryptSync(
            password.normalize("NFKC"),
            saltHex,
            64,
            {
              N: 16384,
              r: 16, 
              p: 1,
              maxmem: 128 * 16384 * 16 * 2
            }
          );
          
          const targetKeyHex = Array.from(targetKey).map(b => b.toString(16).padStart(2, '0')).join('');
          return targetKeyHex === keyHex;
        },
      },
    }

This eliminated the timeouts and works correctly with existing password hashes. It appears to be compatible with the default algorithm.
Could someone help confirm if there are any security concerns or compatibility issues with this implementation?

<!-- gh-comment-id:3024999773 --> @atsuki44 commented on GitHub (Jul 1, 2025): I encountered the same CPU time limit issue when deploying better-auth with Hono on Cloudflare Workers. I was able to resolve the timeout by using a custom hash function with Node.js crypto: ```ts emailAndPassword: { enabled: true, password: { hash: async (password) => { const salt = crypto.getRandomValues(new Uint8Array(16)); const saltHex = Array.from(salt).map(b => b.toString(16).padStart(2, '0')).join(''); const key = scryptSync( password.normalize("NFKC"), saltHex, 64, { N: 16384, r: 16, p: 1, maxmem: 128 * 16384 * 16 * 2 } ); const keyHex = Array.from(key).map(b => b.toString(16).padStart(2, '0')).join(''); return `${saltHex}:${keyHex}`; }, verify: async ({ hash, password }) => { const [saltHex, keyHex] = hash.split(":"); const targetKey = scryptSync( password.normalize("NFKC"), saltHex, 64, { N: 16384, r: 16, p: 1, maxmem: 128 * 16384 * 16 * 2 } ); const targetKeyHex = Array.from(targetKey).map(b => b.toString(16).padStart(2, '0')).join(''); return targetKeyHex === keyHex; }, }, } ``` This eliminated the timeouts and works correctly with existing password hashes. It appears to be compatible with the default algorithm. Could someone help confirm if there are any security concerns or compatibility issues with this implementation?
Author
Owner

@JoshuamURD commented on GitHub (Jul 21, 2025):

I am also having this issue with better auth, nuxthub (cloudflare workers) and neondb

<!-- gh-comment-id:3095795653 --> @JoshuamURD commented on GitHub (Jul 21, 2025): I am also having this issue with better auth, nuxthub (cloudflare workers) and neondb
Author
Owner

@Gonzalo-Bruna commented on GitHub (Jul 21, 2025):

I am also having this issue with better auth, nuxthub (cloudflare workers) and neondb

How are you using neondb with better auth? because I am using it and I am having no issues.
Show me the code where you create the better-auth instance and I'll help you solve it

<!-- gh-comment-id:3096873831 --> @Gonzalo-Bruna commented on GitHub (Jul 21, 2025): > I am also having this issue with better auth, nuxthub (cloudflare workers) and neondb How are you using neondb with better auth? because I am using it and I am having no issues. Show me the code where you create the better-auth instance and I'll help you solve it
Author
Owner

@JoshuamURD commented on GitHub (Jul 29, 2025):

I am also having this issue with better auth, nuxthub (cloudflare workers) and neondb

How are you using neondb with better auth? because I am using it and I am having no issues. Show me the code where you create the better-auth instance and I'll help you solve it

Thanks for the offer but I managed to figure out myself. A word of warning to others facing the same issue. Cloudflare workers does not differentiate an error in the better auth setup and exceeding CPU time limit.

In my case, turns out I had two issues (which caused the same CPU exceeded error). I had a CORS middleware on my Nuxt app that wasn't configured properly by not allowing calls to /api/auth/[...all]. I also hadn't set a production server baseURL for better auth and it was instead just using localhost.

These issues were difficult to diagnose because both separately gave the same error.

<!-- gh-comment-id:3130772606 --> @JoshuamURD commented on GitHub (Jul 29, 2025): > > I am also having this issue with better auth, nuxthub (cloudflare workers) and neondb > > How are you using neondb with better auth? because I am using it and I am having no issues. Show me the code where you create the better-auth instance and I'll help you solve it Thanks for the offer but I managed to figure out myself. A word of warning to others facing the same issue. Cloudflare workers does not differentiate an error in the better auth setup and exceeding CPU time limit. In my case, turns out I had two issues (which caused the same CPU exceeded error). I had a CORS middleware on my Nuxt app that wasn't configured properly by not allowing calls to /api/auth/[...all]. I also hadn't set a production server baseURL for better auth and it was instead just using localhost. These issues were difficult to diagnose because both separately gave the same error.
Author
Owner

@MathiasWP commented on GitHub (Aug 28, 2025):

In my case this happened because of a floating promise. Using the waitUntil method helped me: https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil

<!-- gh-comment-id:3235069660 --> @MathiasWP commented on GitHub (Aug 28, 2025): In my case this happened because of a floating promise. Using the `waitUntil` method helped me: https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil
Author
Owner

@saturnonearth commented on GitHub (Aug 29, 2025):

In my case this happened because of a floating promise. Using the waitUntil method helped me: https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil

At what point do you use this?

<!-- gh-comment-id:3235362670 --> @saturnonearth commented on GitHub (Aug 29, 2025): > In my case this happened because of a floating promise. Using the `waitUntil` method helped me: https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil At what point do you use this?
Author
Owner

@techjandro commented on GitHub (Aug 31, 2025):

I was/(am?) dealing with the same issue using Cloudflare Workers, Hono, Drizzle/D1, Better Auth.

For some reason the API endpoints for sign-in/email, get-session and create-user (and maybe others) were taking around ~5 seconds, inspecting into details show database operations involved in these to be just milliseconds but the specific hashing "queries" was the actual time-consuming "step" inside it. I may be wrong about this.

But I didn't want to replace the hashing or anything related with that because I'm not quite sure how unsafe that is.

I replicated the @ariburaco and other users implementation to my stack and it seems to go better now, basically just trying to instantiate the DB and auth client inside the request itself, I'm still confused on how that makes a difference tbh, to my eyes it should run just the same but I may be missing something on this. I would appreciate any insights on that 🫶

But for now it seems to run a lot better most of the times, for comparisong the ~4/~5 seconds operations in the images were before adjusting the implementation -vs- ~hundreds of milliseconds right after.

Sometimes it stills peaks up to ~2 seconds (I'm fine with that for now I guess...) but I would love to know in the future how to improve the situation, maybe not only for time consuming concerns but as for how efective and optimal the implementation and integration of these tech stack is.

Image Image
<!-- gh-comment-id:3240124350 --> @techjandro commented on GitHub (Aug 31, 2025): I was/_(am?)_ dealing with the same issue using Cloudflare Workers, Hono, Drizzle/D1, Better Auth. For some reason the API endpoints for **sign-in/email**, **get-session** and **create-user** (and maybe others) were taking around ~5 seconds, inspecting into details show database operations involved in these to be just milliseconds but the specific hashing "queries" was the actual time-consuming "step" inside it. I may be wrong about this. But I didn't want to replace the hashing or anything related with that because I'm not quite sure how unsafe that is. I replicated the @ariburaco and other users implementation to my stack and it seems to go better now, basically just trying to instantiate the DB and auth client inside the request itself, I'm still confused on how that makes a difference tbh, to my eyes it should run just the same but I may be missing something on this. I would appreciate any insights on that 🫶 But for now it seems to run a lot better most of the times, for comparisong the ~4/~5 seconds operations in the images were before adjusting the implementation -vs- ~hundreds of milliseconds right after. Sometimes it stills peaks up to ~2 seconds (I'm fine with that for now I guess...) but I would love to know in the future how to improve the situation, maybe not only for time consuming concerns but as for how efective and optimal the implementation and integration of these tech stack is. <img width="1231" height="592" alt="Image" src="https://github.com/user-attachments/assets/c3b40e56-c731-4d82-a4ed-8fee1c82e676" /> <img width="1231" height="298" alt="Image" src="https://github.com/user-attachments/assets/271ef592-e926-4959-be8c-a423d95f8d8a" />
Author
Owner

@MathiasWP commented on GitHub (Sep 1, 2025):

In my case this happened because of a floating promise. Using the waitUntil method helped me: developers.cloudflare.com/workers/runtime-apis/context#waituntil

At what point do you use this?

Caching the user

<!-- gh-comment-id:3241240468 --> @MathiasWP commented on GitHub (Sep 1, 2025): > > In my case this happened because of a floating promise. Using the `waitUntil` method helped me: [developers.cloudflare.com/workers/runtime-apis/context#waituntil](https://developers.cloudflare.com/workers/runtime-apis/context/#waituntil) > > At what point do you use this? Caching the user
Author
Owner

@bruisedpayne commented on GitHub (Dec 6, 2025):

I faced a similar issue. 1101 error when accessing any endpoint that uses better auth instance. I solved it by using AsyncLocalStorage.

b6185102e5

thank you @ariburaco for pointing me in the right direction

<!-- gh-comment-id:3620685724 --> @bruisedpayne commented on GitHub (Dec 6, 2025): I faced a similar issue. 1101 error when accessing any endpoint that uses better auth instance. I solved it by using AsyncLocalStorage. https://github.com/bruisedpayne/cybertown/commit/b6185102e56f746affbb08b1b7d0852212e7e0e1 thank you @ariburaco for pointing me in the right direction
Author
Owner

@hexcowboy commented on GitHub (Mar 7, 2026):

There may be many reasons for this error but there is one certain thing that can fix this if you have this pattern in your worker: singletons should not be used in Cloudflare workers.

If you're using a coding agent, ask it if you're using this pattern for either auth or database, and if either is true that's likely your issue. Just create a new database/auth instance on each request, which is the correct way to do so in workers.

<!-- gh-comment-id:4017106930 --> @hexcowboy commented on GitHub (Mar 7, 2026): There may be many reasons for this error but there is one certain thing that can fix this if you have this pattern in your worker: singletons should not be used in Cloudflare workers. If you're using a coding agent, ask it if you're using this pattern for either auth or database, and if either is true that's likely your issue. Just create a new database/auth instance on each request, which is the correct way to do so in workers.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#8525