[GH-ISSUE #6690] Cloudflare Workers: createRequire(import.meta.url) fails at module initialization #19225

Closed
opened 2026-04-15 18:04:20 -05:00 by GiteaMirror · 6 comments
Owner

Originally created by @praneybehl on GitHub (Dec 11, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6690

Description

BetterAuth fails to load on Cloudflare Workers due to createRequire(import.meta.url) throwing at module initialization time. The error occurs because import.meta.url is undefined in Cloudflare Workers' bundled output.

Error Message

TypeError: The argument 'path' must be a file URL object, file URL string, or absolute path string. Received 'undefined'
  at createRequire (node:module)
  at index.js:25297:33

Environment

  • BetterAuth: 1.4.6
  • Wrangler: 4.53.0
  • Hono: 4.7.0
  • Runtime: Cloudflare Workers with nodejs_compat_v2

Reproduction

  1. Create a Hono + BetterAuth project for Cloudflare Workers
  2. Configure wrangler.toml:
compatibility_date = "2025-09-01"
compatibility_flags = ["nodejs_compat_v2"]
  1. Run wrangler dev
  2. Error occurs immediately at module load

Root Cause Analysis

The issue is in Rolldown's bundler runtime helpers in dist/chunk-DieNfLhd.mjs:

import { createRequire } from "node:module";
var __require = /* @__PURE__ */ createRequire(import.meta.url);

This __require is used throughout the bundle to dynamically require Node.js built-ins:

// dist/esm-CyZgw_uF.mjs (~20 calls - postgres driver code)
const util = __require("util");
const nodeCrypto = __require("crypto");
const fs = config.sslcert ? __require("fs") : null;
const dns = __require("dns");
const { EventEmitter } = __require("events");
return new (__require("net")).Socket();
return __require("tls").connect(options);

// dist/promise-CL99vzc3.mjs (~15 calls)
var buffer = __require("buffer");
var StringDecoder = __require("string_decoder").StringDecoder;
streamModule = __require("stream");
const crypto = __require("crypto");

Key problem: The error occurs at module initialization time - before any user code runs. This means:

  • The error happens even if code paths using fs, net, tls are never executed
  • Users cannot catch or handle the error
  • Even nodejs_compat_v2 doesn't help because import.meta.url remains undefined in bundled output

Suggested Solutions

// Instead of:
var __require = createRequire(import.meta.url);

// Use:
var __require = (() => {
  if (typeof import.meta?.url === 'string' && import.meta.url.startsWith('file:')) {
    try {
      const { createRequire } = await import('node:module');
      return createRequire(import.meta.url);
    } catch {}
  }
  // Fallback for edge runtimes
  return (id) => {
    throw new Error(`Dynamic require of "${id}" is not supported. Use ESM imports.`);
  };
})();

Option 2: Static ESM Imports

Replace dynamic requires with static ESM imports that work on Workers:

// Instead of:
const crypto = __require("crypto");

// Use:
import crypto from "node:crypto";

Option 3: Separate Edge Bundle

Create an edge-specific entry point that:

  • Uses only ESM imports
  • Uses Web Crypto API instead of node:crypto where possible
  • Excludes Node.js-only code paths (TCP sockets, filesystem)

Current Workaround

Users can work around this by creating a polyfill and using Wrangler's module aliasing:

wrangler.toml:

[alias]
"node:module" = "./src/polyfills/node-module.ts"

src/polyfills/node-module.ts:

export function createRequire(_filename: string | URL): NodeRequire {
  const req = function require(id: string): never {
    throw new Error(`Cannot use require() in Cloudflare Workers.`);
  };
  req.resolve = () => { throw new Error('Not supported'); };
  req.resolve.paths = () => null;
  req.cache = {};
  req.extensions = {};
  req.main = undefined;
  return req as unknown as NodeRequire;
}
export function isBuiltin() { return false; }
export function findSourceMap() { return undefined; }
export function syncBuiltinESMExports() {}
export default { createRequire, isBuiltin, findSourceMap, syncBuiltinESMExports };
  • #1375 - Support Cloudflare Workers without nodejs_compat
  • #175 - Better Auth with Cloudflare Workers
  • #6613 - Upgrade from 1.3 to 1.4 errors on Cloudflare Workers

Additional Context

This is likely a build/bundling configuration issue rather than a code issue. The __require pattern comes from Rolldown's CommonJS interop when bundling dependencies that use require(). Configuring Rolldown to either:

  1. Externalize Node.js built-ins
  2. Use a edge-runtime-aware createRequire shim
  3. Tree-shake unused code paths

...would resolve this for all edge runtime users.

Originally created by @praneybehl on GitHub (Dec 11, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6690 ## Description BetterAuth fails to load on Cloudflare Workers due to `createRequire(import.meta.url)` throwing at module initialization time. The error occurs because `import.meta.url` is `undefined` in Cloudflare Workers' bundled output. ### Error Message ``` TypeError: The argument 'path' must be a file URL object, file URL string, or absolute path string. Received 'undefined' at createRequire (node:module) at index.js:25297:33 ``` ### Environment - BetterAuth: 1.4.6 - Wrangler: 4.53.0 - Hono: 4.7.0 - Runtime: Cloudflare Workers with `nodejs_compat_v2` ### Reproduction 1. Create a Hono + BetterAuth project for Cloudflare Workers 2. Configure `wrangler.toml`: ```toml compatibility_date = "2025-09-01" compatibility_flags = ["nodejs_compat_v2"] ``` 3. Run `wrangler dev` 4. Error occurs immediately at module load ### Root Cause Analysis The issue is in Rolldown's bundler runtime helpers in `dist/chunk-DieNfLhd.mjs`: ```javascript import { createRequire } from "node:module"; var __require = /* @__PURE__ */ createRequire(import.meta.url); ``` This `__require` is used throughout the bundle to dynamically require Node.js built-ins: ```javascript // dist/esm-CyZgw_uF.mjs (~20 calls - postgres driver code) const util = __require("util"); const nodeCrypto = __require("crypto"); const fs = config.sslcert ? __require("fs") : null; const dns = __require("dns"); const { EventEmitter } = __require("events"); return new (__require("net")).Socket(); return __require("tls").connect(options); // dist/promise-CL99vzc3.mjs (~15 calls) var buffer = __require("buffer"); var StringDecoder = __require("string_decoder").StringDecoder; streamModule = __require("stream"); const crypto = __require("crypto"); ``` **Key problem:** The error occurs at **module initialization time** - before any user code runs. This means: - The error happens even if code paths using `fs`, `net`, `tls` are never executed - Users cannot catch or handle the error - Even `nodejs_compat_v2` doesn't help because `import.meta.url` remains undefined in bundled output ### Suggested Solutions #### Option 1: Conditional Runtime Helper (Recommended) ```javascript // Instead of: var __require = createRequire(import.meta.url); // Use: var __require = (() => { if (typeof import.meta?.url === 'string' && import.meta.url.startsWith('file:')) { try { const { createRequire } = await import('node:module'); return createRequire(import.meta.url); } catch {} } // Fallback for edge runtimes return (id) => { throw new Error(`Dynamic require of "${id}" is not supported. Use ESM imports.`); }; })(); ``` #### Option 2: Static ESM Imports Replace dynamic requires with static ESM imports that work on Workers: ```javascript // Instead of: const crypto = __require("crypto"); // Use: import crypto from "node:crypto"; ``` #### Option 3: Separate Edge Bundle Create an edge-specific entry point that: - Uses only ESM imports - Uses Web Crypto API instead of `node:crypto` where possible - Excludes Node.js-only code paths (TCP sockets, filesystem) ### Current Workaround Users can work around this by creating a polyfill and using Wrangler's module aliasing: **`wrangler.toml`:** ```toml [alias] "node:module" = "./src/polyfills/node-module.ts" ``` **`src/polyfills/node-module.ts`:** ```typescript export function createRequire(_filename: string | URL): NodeRequire { const req = function require(id: string): never { throw new Error(`Cannot use require() in Cloudflare Workers.`); }; req.resolve = () => { throw new Error('Not supported'); }; req.resolve.paths = () => null; req.cache = {}; req.extensions = {}; req.main = undefined; return req as unknown as NodeRequire; } export function isBuiltin() { return false; } export function findSourceMap() { return undefined; } export function syncBuiltinESMExports() {} export default { createRequire, isBuiltin, findSourceMap, syncBuiltinESMExports }; ``` ### Related Issues - #1375 - Support Cloudflare Workers without nodejs_compat - #175 - Better Auth with Cloudflare Workers - #6613 - Upgrade from 1.3 to 1.4 errors on Cloudflare Workers ### Additional Context This is likely a build/bundling configuration issue rather than a code issue. The `__require` pattern comes from Rolldown's CommonJS interop when bundling dependencies that use `require()`. Configuring Rolldown to either: 1. Externalize Node.js built-ins 2. Use a edge-runtime-aware `createRequire` shim 3. Tree-shake unused code paths ...would resolve this for all edge runtime users.
GiteaMirror added the locked label 2026-04-15 18:04:20 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Dec 11, 2025):

This error happens because the BetterAuth bundle (via Rolldown) uses createRequire(import.meta.url) for CommonJS interop, but import.meta.url is undefined in Cloudflare Workers, causing a crash at module initialization—before any user code runs. The nodejs_compat_v2 flag does not fix this, since the problem is with the bundler output, not the runtime itself [#1375, #175, #6613, #6635].

The most robust workaround is to create a polyfill for node:module that throws on use, and alias it in your wrangler.toml:

[alias]
"node:module" = "./src/polyfills/node-module.ts"
// src/polyfills/node-module.ts
export function createRequire(_filename: string | URL): NodeRequire {
  const req = function require(id: string): never {
    throw new Error(`Cannot use require() in Cloudflare Workers.`);
  };
  req.resolve = () => { throw new Error('Not supported'); };
  req.resolve.paths = () => null;
  req.cache = {};
  req.extensions = {};
  req.main = undefined;
  return req as unknown as NodeRequire;
}
export function isBuiltin() { return false; }
export function findSourceMap() { return undefined; }
export function syncBuiltinESMExports() {}
export default { createRequire, isBuiltin, findSourceMap, syncBuiltinESMExports };

This lets the Worker start, but any code path that actually tries to use require() will still throw. This workaround is tested and works with Wrangler 4.53.0, BetterAuth 1.4.6, and Hono 4.7.0 [#175, #1375].

Long-term, the maintainers are aware and are working on better edge compatibility. Some beta versions (e.g., 1.4.6-beta.4, 1.4.7-beta.2) reportedly fix this for some users [#6635]. There’s also a community plugin better-auth-cloudflare for deeper integration with Workers and Cloudflare services [#4212].

If you need full compatibility, consider downgrading to 1.4.5 or earlier, or testing the latest beta. For production, always instantiate the auth client inside each request handler (not as a singleton) to avoid context issues. Full edge support is still a work in progress [#6613, #1375].

To reply, just mention @dosu.


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

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3640544168 --> @dosubot[bot] commented on GitHub (Dec 11, 2025): <!-- Answer --> This error happens because the BetterAuth bundle (via Rolldown) uses `createRequire(import.meta.url)` for CommonJS interop, but `import.meta.url` is `undefined` in Cloudflare Workers, causing a crash at module initialization—before any user code runs. The `nodejs_compat_v2` flag does not fix this, since the problem is with the bundler output, not the runtime itself [[#1375](https://github.com/better-auth/better-auth/issues/1375), [#175](https://github.com/better-auth/better-auth/issues/175), [#6613](https://github.com/better-auth/better-auth/issues/6613), [#6635](https://github.com/better-auth/better-auth/issues/6635)]. The most robust workaround is to create a polyfill for `node:module` that throws on use, and alias it in your `wrangler.toml`: ```toml [alias] "node:module" = "./src/polyfills/node-module.ts" ``` ```ts // src/polyfills/node-module.ts export function createRequire(_filename: string | URL): NodeRequire { const req = function require(id: string): never { throw new Error(`Cannot use require() in Cloudflare Workers.`); }; req.resolve = () => { throw new Error('Not supported'); }; req.resolve.paths = () => null; req.cache = {}; req.extensions = {}; req.main = undefined; return req as unknown as NodeRequire; } export function isBuiltin() { return false; } export function findSourceMap() { return undefined; } export function syncBuiltinESMExports() {} export default { createRequire, isBuiltin, findSourceMap, syncBuiltinESMExports }; ``` This lets the Worker start, but any code path that actually tries to use `require()` will still throw. This workaround is tested and works with Wrangler 4.53.0, BetterAuth 1.4.6, and Hono 4.7.0 [[#175](https://github.com/better-auth/better-auth/issues/175), [#1375](https://github.com/better-auth/better-auth/issues/1375)]. Long-term, the maintainers are aware and are working on better edge compatibility. Some beta versions (e.g., `1.4.6-beta.4`, `1.4.7-beta.2`) reportedly fix this for some users [[#6635](https://github.com/better-auth/better-auth/issues/6635)]. There’s also a community plugin [`better-auth-cloudflare`](https://github.com/zpg6/better-auth-cloudflare) for deeper integration with Workers and Cloudflare services [[#4212](https://github.com/better-auth/better-auth/pull/4212)]. If you need full compatibility, consider downgrading to 1.4.5 or earlier, or testing the latest beta. For production, always instantiate the auth client inside each request handler (not as a singleton) to avoid context issues. Full edge support is still a work in progress [[#6613](https://github.com/better-auth/better-auth/issues/6613), [#1375](https://github.com/better-auth/better-auth/issues/1375)]. <!-- 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/b0baf730-3477-4f09-b434-366a5000d560?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/b0baf730-3477-4f09-b434-366a5000d560?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/b0baf730-3477-4f09-b434-366a5000d560?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/b0baf730-3477-4f09-b434-366a5000d560?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/b0baf730-3477-4f09-b434-366a5000d560?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/b0baf730-3477-4f09-b434-366a5000d560?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/b0baf730-3477-4f09-b434-366a5000d560?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20error%20happens%20because%20the%20BetterAuth%20bundle%20%28via%20Rolldown%29%20uses%20%60createRequire%28import.meta.url%29%60%20for%20CommonJS%20interop%2C%20but%20%60import.meta.url%60%20is%20%60undefined%60%20in%20Cloudflare%20Workers%2C%20causing%20a%20crash%20at%20module%20initialization%E2%80%94before%20any%20user%20code%20runs.%20The%20%60nodejs_compat_v2%60%20flag%20does%20not%20fix%20this%2C%20since%20the%20problem%20is%20with%20the%20bundler%20output%2C%20not%20the%20runtime%20itself%20%5B%5B%231375%5D%28https%3A//github.com/better-auth/better-auth/issues/1375%29%2C%20%5B%23175%5D%28https%3A//github.com/better-auth/better-auth/issues/175%29%2C%20%5B%236613%5D%28https%3A//github.com/better-auth/better-auth/issues/6613%29%2C%20%5B%236635%5D%28https%3A//github.com/better-auth/better-auth/issues/6635%29%5D.%0A%0AThe%20most%20robust%20workaround%20is%20to%20create%20a%20polyfill%20for%20%60node%3Amodule%60%20that%20throws%20on%20use%2C%20and%20alias%20it%20in%20your%20%60wrangler.toml%60%3A%0A%0A%60%60%60toml%0A%5Balias%5D%0A%22node%3Amodule%22%20%3D%20%22./src/polyfills/node-module.ts%22%0A%60%60%60%0A%0A%60%60%60ts%0A//%20src/polyfills/node-module.ts%0Aexport%20function%20createRequire%28_filename%3A%20string%20%7C%20URL%29%3A%20NodeRequire%20%7B%0A%20%20const%20req%20%3D%20function%20require%28id%3A%20string%29%3A%20never%20%7B%0A%20%20%20%20throw%20new%20Error%28%60Cannot%20use%20require%28%29%20in%20Cloudflare%20Workers.%60%29%3B%0A%20%20%7D%3B%0A%20%20req.resolve%20%3D%20%28%29%20%3D%3E%20%7B%20throw%20new%20Error%28%27Not%20supported%27%29%3B%20%7D%3B%0A%20%20req.resolve.paths%20%3D%20%28%29%20%3D%3E%20null%3B%0A%20%20req.cache%20%3D%20%7B%7D%3B%0A%20%20req.extensions%20%3D%20%7B%7D%3B%0A%20%20req.main%20%3D%20undefined%3B%0A%20%20return%20req%20as%20unknown%20as%20NodeRequire%3B%0A%7D%0Aexport%20function%20isBuiltin%28%29%20%7B%20return%20false%3B%20%7D%0Aexport%20function%20findSourceMap%28%29%20%7B%20return%20undefined%3B%20%7D%0Aexport%20function%20syncBuiltinESMExports%28%29%20%7B%7D%0Aexport%20default%20%7B%20createRequire%2C%20isBuiltin%2C%20findSourceMap%2C%20syncBuiltinESMExports%20%7D%3B%0A%60%60%60%0A%0AThis%20lets%20the%20Worker%20start%2C%20but%20any%20code%20path%20that%20actually%20tries%20to%20use%20%60require%28%29%60%20will%20still%20throw.%20This%20workaround%20is%20tested%20and%20works%20with%20Wrangler%204.53.0%2C%20BetterAuth%201.4.6%2C%20and%20Hono%204.7.0%20%5B%5B%23175%5D%28https%3A//github.com/better-auth/better-auth/issues/175%29%2C%20%5B%231375%5D%28https%3A//github.com/better-auth/better-auth/issues/1375%29%5D.%0A%0ALong-term%2C%20the%20maintainers%20are%20aware%20and%20are%20working%20on%20better%20edge%20compatibility.%20Some%20beta%20versions%20%28e.g.%2C%20%601.4.6-beta.4%60%2C%20%601.4.7-beta.2%60%29%20reportedly%20fix%20this%20for%20some%20users%20%5B%5B%236635%5D%28https%3A//github.com/better-auth/better-auth/issues/6635%29%5D.%20There%E2%80%99s%20also%20a%20community%20plugin%20%5B%60better-auth-cloudflare%60%5D%28https%3A//github.com/zpg6/better-auth-cloudflare%29%20for%20deeper%20integration%20with%20Workers%20and%20Cloudflare%20services%20%5B%5B%234212%5D%28https%3A//github.com/better-auth/better-auth/pull/4212%29%5D.%0A%0AIf%20you%20need%20full%20compatibility%2C%20consider%20downgrading%20to%201.4.5%20or%20earlier%2C%20or%20testing%20the%20latest%20beta.%20For%20production%2C%20always%20instantiate%20the%20auth%20client%20inside%20each%20request%20handler%20%28not%20as%20a%20singleton%29%20to%20avoid%20context%20issues.%20Full%20edge%20support%20is%20still%20a%20work%20in%20progress%20%5B%5B%236613%5D%28https%3A//github.com/better-auth/better-auth/issues/6613%29%2C%20%5B%231375%5D%28https%3A//github.com/better-auth/better-auth/issues/1375%29%5D.)&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/6690)
Author
Owner

@fokion commented on GitHub (Dec 11, 2025):

The issue starts after version 1.3.34

<!-- gh-comment-id:3643978952 --> @fokion commented on GitHub (Dec 11, 2025): The issue starts after version 1.3.34
Author
Owner

@fokion commented on GitHub (Dec 11, 2025):

https://github.com/better-auth/better-auth/issues/6665

<!-- gh-comment-id:3643984968 --> @fokion commented on GitHub (Dec 11, 2025): https://github.com/better-auth/better-auth/issues/6665
Author
Owner

@GautamBytes commented on GitHub (Dec 12, 2025):

@praneybehl try this , hope it fixes the issue
https://github.com/better-auth/better-auth/issues/6665#issuecomment-3637675315

@maxwell we can close this issue and #6665 as well!

<!-- gh-comment-id:3645584395 --> @GautamBytes commented on GitHub (Dec 12, 2025): @praneybehl try this , hope it fixes the issue https://github.com/better-auth/better-auth/issues/6665#issuecomment-3637675315 @maxwell we can close this issue and #6665 as well!
Author
Owner

@himself65 commented on GitHub (Jan 3, 2026):

It's related to our bundler. We have fixed this in the latest version. Please have a try :)

<!-- gh-comment-id:3706908620 --> @himself65 commented on GitHub (Jan 3, 2026): It's related to our bundler. We have fixed this in the latest version. Please have a try :)
Author
Owner

@jkomyno commented on GitHub (Jan 9, 2026):

It's related to our bundler. We have fixed this in the latest version. Please have a try :)

Hi, could you please point us to how you fixed the issue?

<!-- gh-comment-id:3729045647 --> @jkomyno commented on GitHub (Jan 9, 2026): > It's related to our bundler. We have fixed this in the latest version. Please have a try :) Hi, could you please point us to how you fixed the issue?
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19225