Back to Better Auth

Postmortem: Cloudflare Workers `createRequire` Runtime Helper

.postmortem/cloudflare-create-require-runtime.md

1.7.05.0 KB
Original Source

Postmortem: Cloudflare Workers createRequire Runtime Helper

Issue Reference

Summary

[email protected] 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:

js
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. 1.4.6 reports (#6638, #6665, #6690): Cloudflare Workers crashed during startup because bundled output eagerly called createRequire(import.meta.url).
  2. PR #6704: Added a Cloudflare smoke test that fails if Wrangler output contains createRequire, node:module, or selected Node-only modules.
  3. PR #9657: Added getHttpTestInstance to better-auth/test. The helper statically imported listhen, which pulled in CommonJS-oriented Node server dependencies when the better-auth multi-entry package was built.
  4. 1.7.0-beta.4 and beta.5: The published package again contained the eager shared rolldown __require helper. 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:

  1. Wrangler's final Worker bundle still must not contain createRequire, node:fs, or node:module.
  2. The built better-auth rolldown runtime helper must not contain createRequire, node:module, or __require.

Lesson Learned

  1. Package runtime helpers are part of the edge-runtime contract. Checking only the final app bundle can miss a published-package regression.
  2. 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.
  3. Do not add external listener/server dependencies to better-auth/test unless the built package runtime is checked afterward. Prefer Node built-ins for test helpers when they are sufficient.
  4. Generated dist can be stale locally. Rebuild before declaring this class fixed or unaffected.

Prevention

  1. Keep the Cloudflare smoke test's direct assertion on packages/better-auth/dist/_virtual/_rolldown/runtime.mjs.
  2. When reviewing changes to packages/better-auth/src/test-utils, rebuild better-auth and scan the generated runtime helper for createRequire, node:module, and __require.
  3. 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.