Skip to content

fix(presets): guard against ReferenceError when process is not defined#410

Open
Zelys-DFKH wants to merge 5 commits into
t3-oss:mainfrom
Zelys-DFKH:fix/preset-browser-guard
Open

fix(presets): guard against ReferenceError when process is not defined#410
Zelys-DFKH wants to merge 5 commits into
t3-oss:mainfrom
Zelys-DFKH:fix/preset-browser-guard

Conversation

@Zelys-DFKH

Copy link
Copy Markdown

Problem

Calling any server preset (vercel(), uploadthing(), railway(), etc.) in a browser environment without a process polyfill crashes with:

Uncaught ReferenceError: process is not defined
    vercel presets-zod.js:43

This happens because the preset functions call createEnv({ runtimeEnv: process.env, ... }), and process.env is evaluated at call time, before any guard can fire.

Fixes #407.

Fix

Each preset file (presets-zod.ts, presets-arktype.ts, presets-valibot.ts) now defines a private getter:

const serverEnv = (): Record<string, string | undefined> =>
  typeof process !== "undefined" ? process.env : {};

Each server preset passes serverEnv() as runtimeEnv. The function is called at preset invocation time, so:

  • In Node.js / Bun / Deno: returns process.env as before
  • In browsers without a process polyfill: returns {} — all preset env vars are optional, so validation passes

vite and wxt presets already use import.meta.env and are unchanged.

The same guard is applied to the runtimeEnv fallback inside createEnv (packages/core/src/index.ts), protecting against the case where a caller omits runtimeEnv entirely in a browser context.

Why not a module-level constant?

A module-level const serverRuntimeEnv = typeof process !== "undefined" ? process.env : {} evaluates at import time. The test suite reassigns process.env inside a describe block, which runs during collection after imports complete, so a const captures a stale reference and breaks the existing "with built-in preset" test.

Tests

Regression tests added to all four smoke suites (zod v3, zod v4, arktype, valibot):

test("server presets do not throw when process is not defined", () => {
  vi.stubGlobal("process", undefined);
  try {
    expect(() => vercel()).not.toThrow();
  } finally {
    vi.unstubAllGlobals();
  }
});

This test fails on pre-fix code (accessing process.env where process is undefined throws TypeError) and passes on the fix.

Zelys-DFKH added 5 commits May 3, 2026 14:28
Module-level const evaluated at import time; the test suite reassigns
process.env in a describe block, so the const captured a stale
reference. A getter function evaluated at call time preserves the
original behavior while remaining safe in environments where process
is undefined.
@vercel

vercel Bot commented May 3, 2026

Copy link
Copy Markdown

@Zelys-DFKH is attempting to deploy a commit to the t3-oss Team on Vercel.

A member of the Team first needs to authorize it.

@Zelys-DFKH Zelys-DFKH marked this pull request as ready for review May 3, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Vercel Preset raises a browser error

1 participant