Cf Workers Per Request Io
Cloudflare Workers — Never Cache Per-Request I/O Across Requests
Section titled “Cloudflare Workers — Never Cache Per-Request I/O Across Requests”CF Workers forbid using an I/O object — a DB/Hyperdrive socket, a stream, a
request/response body, a fetch connection — created in one request handler from a
DIFFERENT request handler. Reusing one throws:
Cannot perform I/O on behalf of a different request ... (I/O type: ...).
So do NOT cache anything that holds a per-request connection in module scope.
The DB client from createDB(env) opens a per-request socket (Hyperdrive/Neon);
anything wrapping it (a Better Auth instance via the drizzle adapter, a service
bound to db) inherits that constraint.
- Build per request, every request:
createDB(env)and anything binding it —createAuth(createDB(env), …), drizzle adapters, service instances holdingdb. Construct them INSIDE the request handler and return fresh. Never memoize in a module-levellet/singleton. - Module-level singletons are ONLY safe for stateless, connection-free objects —
e.g. an oRPC
OpenAPIHandlerbuilt from the static router (dbis injected per request via context, so the handler holds no socket). If in doubt, don’t cache. env: read per request fromcloudflare:workers(seesecret-env-typing/ theinfisical-secretsskill); don’t stash it module-global either.
Why it bites silently
Section titled “Why it bites silently”A cached instance “works” on the request that created it and on single-request
probes. It fails only on a LATER request in the same warm isolate — e.g. the OAuth
sign-in request opens the DB connection, the OAuth callback request reuses the
cached auth instance and the verifications lookup throws → Better Auth redirects
to ?error=please_restart_the_process and the user bounces back to login with no
session. Connection-free paths (an empty get-session with no cookie) hide it.
Treat any “works once, then breaks on the next request” Worker bug as cross-request
I/O reuse until ruled out.