5.0 KiB
Postmortem: Cloudflare Workers createRequire Runtime Helper
Issue Reference
Summary
better-auth@1.7.0-beta.4 and 1.7.0-beta.5 reintroduced the Cloudflare
Workers startup crash previously seen in the 1.4.6 release line. The published
better-auth package emitted this shared rolldown runtime helper:
import { createRequire } from "node:module";
var __require = /* @__PURE__ */ createRequire(import.meta.url);
Cloudflare Workers can leave import.meta.url undefined in bundled output, so
the package crashed at module evaluation time before application code ran. The
crash affected unrelated subpaths such as better-auth/db because the helper
lived in the shared _virtual/_rolldown/runtime.mjs file imported by many
entries, not only by the Node-specific code that needed CommonJS interop.
Recurrence History
This class of bug has already recurred once:
- 1.4.6 reports (#6638, #6665, #6690): Cloudflare Workers crashed during
startup because bundled output eagerly called
createRequire(import.meta.url). - PR #6704: Added a Cloudflare smoke test that fails if Wrangler output
contains
createRequire,node:module, or selected Node-only modules. - PR #9657: Added
getHttpTestInstancetobetter-auth/test. The helper statically importedlisthen, which pulled in CommonJS-oriented Node server dependencies when thebetter-authmulti-entry package was built. - 1.7.0-beta.4 and beta.5: The published package again contained the eager
shared rolldown
__requirehelper. The existing smoke test did not fail because it checked Wrangler's final bundle, not the published package runtime helper itself.
Root Cause
Test-only dependencies polluted the shared package runtime
packages/better-auth/src/test-utils/index.ts statically re-exported
http-test-instance.ts, and that file statically imported listhen.
Because better-auth/test is built as an entry in the same better-auth
multi-entry build, rolldown included listhen and its transitive dependencies
under dist/node_modules/. Some of those dependencies need CommonJS interop, so
rolldown added __require to the shared runtime helper.
The helper was shared across unrelated entries. A consumer importing
better-auth/db did not import better-auth/test, but still evaluated the
same runtime helper and crashed in Workers.
The existing smoke test checked the wrong boundary
The Cloudflare smoke test checked e2e/smoke/test/fixtures/cloudflare/dist/index.js
after Wrangler had bundled and tree-shaken the fixture. That is still useful,
but it did not inspect the built packages/better-auth/dist/_virtual/_rolldown/runtime.mjs
file that npm publishes.
The package runtime helper is the earlier contract boundary. Once it contains an
eager createRequire(import.meta.url), any downstream bundler that preserves the
helper can crash even if another fixture bundle happens to tree-shake it away.
Fix
getHttpTestInstance now uses node:http.createServer() directly instead of
listhen. The helper only needs an OS-assigned local HTTP port and a
promise-returning close() method, so the external listener dependency was
unnecessary.
The Cloudflare smoke test now checks two boundaries:
- Wrangler's final Worker bundle still must not contain
createRequire,node:fs, ornode:module. - The built
better-authrolldown runtime helper must not containcreateRequire,node:module, or__require.
Lesson Learned
- Package runtime helpers are part of the edge-runtime contract. Checking only the final app bundle can miss a published-package regression.
- A test-only export can poison runtime entries in a multi-entry package. If a subpath is built in the same package, static imports can alter shared helpers used by unrelated subpaths.
- Do not add external listener/server dependencies to
better-auth/testunless the built package runtime is checked afterward. Prefer Node built-ins for test helpers when they are sufficient. - Generated
distcan be stale locally. Rebuild before declaring this class fixed or unaffected.
Prevention
- Keep the Cloudflare smoke test's direct assertion on
packages/better-auth/dist/_virtual/_rolldown/runtime.mjs. - When reviewing changes to
packages/better-auth/src/test-utils, rebuildbetter-authand scan the generated runtime helper forcreateRequire,node:module, and__require. - Reject fixes that only guard
createRequire(import.meta.url)in generated output. The safer fix is to avoid introducing CommonJS interop into the shared runtime helper in the first place.