[GH-ISSUE #572] react-server export condition missing in CF Workers bundle causes deploy failure #123

Open
opened 2026-05-06 12:37:24 +02:00 by BreizhHardware · 3 comments

Originally created by @lucharo on GitHub (Mar 17, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/572

Problem

When deploying a vinext app to Cloudflare Workers, wrangler rejects the upload with:

Uncaught Error: The "react" package in this environment is not configured correctly.
The "react-server" condition must be enabled in any environment that runs React Server Components.

Root cause

vinext's configResolved hook creates its own environments.rsc config that overrides the one from @vitejs/plugin-rsc, which sets resolve.conditions: ["react-server", ...defaultServerConditions] (plugin-rsc source, line 242).

Without the react-server condition, the RSC environment bundles the standard react export (which only exposes __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE). The react-server-dom-webpack server code checks for React.__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE at module init — it's undefined, so it throws.

Additionally, when @cloudflare/vite-plugin re-bundles the RSC + SSR outputs into the final worker entry, even if the RSC build used the correct condition, the worker environment deduplicates to one React copy (the standard one), losing the server internals assignment.

Reproduction

// vite.config.ts
import vinext from "vinext";
import { cloudflare } from "@cloudflare/vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [vinext(), cloudflare()],
});
vite build    # succeeds
vinext deploy # fails with react-server error

Workaround

Users can re-add the condition in their vite.config.ts:

import { defineConfig, defaultServerConditions } from "vite";

export default defineConfig({
  // ...
  environments: {
    rsc: {
      resolve: {
        conditions: ["react-server", ...defaultServerConditions],
      },
    },
  },
});

Plus a post-build patch to fix the worker entry (since the cloudflare plugin's worker environment still bundles without the condition):

// In the built worker entry, replace:
//   ReactSharedInternalsServer = React2.__SERVER_INTERNALS_...;
// with:
//   ReactSharedInternalsServer = (React2.__SERVER_INTERNALS_... = React2.__SERVER_INTERNALS_... || React2.__CLIENT_INTERNALS_...);

Suggested fix

In vinext/dist/index.js around line 1868, the environments.rsc config should include:

resolve: {
  conditions: ["react-server", ...defaultServerConditions],
  // existing external config...
}

The SSR bundle also needs consideration since it's re-bundled into the CF worker entry.

Environment

  • vinext: 0.0.7
  • vite: 7.3.1
  • @cloudflare/vite-plugin: 1.29.0
  • @vitejs/plugin-rsc: 0.5.21
  • react: 19.2.4
  • wrangler: 4.74.0
Originally created by @lucharo on GitHub (Mar 17, 2026). Original GitHub issue: https://github.com/cloudflare/vinext/issues/572 ## Problem When deploying a vinext app to Cloudflare Workers, wrangler rejects the upload with: ``` Uncaught Error: The "react" package in this environment is not configured correctly. The "react-server" condition must be enabled in any environment that runs React Server Components. ``` ## Root cause vinext's `configResolved` hook creates its own `environments.rsc` config that **overrides** the one from `@vitejs/plugin-rsc`, which sets `resolve.conditions: ["react-server", ...defaultServerConditions]` ([plugin-rsc source, line 242](https://github.com/nicolo-ribaudo/vite-plugin-rsc/blob/main/packages/vite-plugin-rsc/src/plugin.ts)). Without the `react-server` condition, the RSC environment bundles the standard `react` export (which only exposes `__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE`). The `react-server-dom-webpack` server code checks for `React.__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE` at module init — it's undefined, so it throws. Additionally, when `@cloudflare/vite-plugin` re-bundles the RSC + SSR outputs into the final worker entry, even if the RSC build used the correct condition, the worker environment deduplicates to one React copy (the standard one), losing the server internals assignment. ## Reproduction ```ts // vite.config.ts import vinext from "vinext"; import { cloudflare } from "@cloudflare/vite-plugin"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [vinext(), cloudflare()], }); ``` ```bash vite build # succeeds vinext deploy # fails with react-server error ``` ## Workaround Users can re-add the condition in their `vite.config.ts`: ```ts import { defineConfig, defaultServerConditions } from "vite"; export default defineConfig({ // ... environments: { rsc: { resolve: { conditions: ["react-server", ...defaultServerConditions], }, }, }, }); ``` Plus a post-build patch to fix the worker entry (since the cloudflare plugin's worker environment still bundles without the condition): ```ts // In the built worker entry, replace: // ReactSharedInternalsServer = React2.__SERVER_INTERNALS_...; // with: // ReactSharedInternalsServer = (React2.__SERVER_INTERNALS_... = React2.__SERVER_INTERNALS_... || React2.__CLIENT_INTERNALS_...); ``` ## Suggested fix In `vinext/dist/index.js` around line 1868, the `environments.rsc` config should include: ```ts resolve: { conditions: ["react-server", ...defaultServerConditions], // existing external config... } ``` The SSR bundle also needs consideration since it's re-bundled into the CF worker entry. ## Environment - vinext: 0.0.7 - vite: 7.3.1 - @cloudflare/vite-plugin: 1.29.0 - @vitejs/plugin-rsc: 0.5.21 - react: 19.2.4 - wrangler: 4.74.0
Author
Owner

@lucharo commented on GitHub (Mar 17, 2026):

Additional findings from minimal reproduction

We confirmed this is a vinext RSC runtime bug, not app-specific:

Minimal test — stripped layout to <html><body>{children}</body></html> (no providers, no CSS, no metadata), page is just <div>Hello vinext</div>:

  • Build succeeds
  • Worker deploys and starts (265-380ms startup)
  • RSC payload IS generated (visible in __VINEXT_RSC_CHUNKS__ script tag)
  • But hydration crashes: TypeError: Cannot read properties of undefined (reading 'length') inside buildFakeCallStackresolveErrorDevprocessFullBinaryRow

Root cause analysis (from deep-diving the bundle):

  1. The RSC build (dist/server/index.js) correctly uses requireReact_reactServer() with proper __SERVER_INTERNALS
  2. The CF worker entry re-bundles this with the standard requireReact() (client), creating two React instances with different internals
  3. The SSR bundle (dist/server/ssr/index.js) also uses client React, but expects server error info format
  4. RSC error serialization sends error info without stack field → SSR's buildFakeCallStack(response, errorInfo.stack, ...) receives undefined → crashes on stack.length

Additional issues we patched around (documented in our PR #132):

  • __vite_rsc_assets_manifest.js not included in worker output (manual copy needed)
  • SSR bundle not included in worker output (manual copy + path rewrite from ../../server/ssr/ to ../server/ssr/)
  • react-server resolve condition dropped from @vitejs/plugin-rsc by vinext's configResolved override

We're moving away from Next.js entirely for our use case, but wanted to document the full findings for other users.

<!-- gh-comment-id:4071726405 --> @lucharo commented on GitHub (Mar 17, 2026): ## Additional findings from minimal reproduction We confirmed this is a vinext RSC runtime bug, not app-specific: **Minimal test** — stripped layout to `<html><body>{children}</body></html>` (no providers, no CSS, no metadata), page is just `<div>Hello vinext</div>`: - Build succeeds - Worker deploys and starts (265-380ms startup) - RSC payload IS generated (visible in `__VINEXT_RSC_CHUNKS__` script tag) - But hydration crashes: `TypeError: Cannot read properties of undefined (reading 'length')` inside `buildFakeCallStack` → `resolveErrorDev` → `processFullBinaryRow` **Root cause analysis** (from deep-diving the bundle): 1. The RSC build (`dist/server/index.js`) correctly uses `requireReact_reactServer()` with proper `__SERVER_INTERNALS` 2. The CF worker entry re-bundles this with the standard `requireReact()` (client), creating **two React instances** with different internals 3. The SSR bundle (`dist/server/ssr/index.js`) also uses client React, but expects server error info format 4. RSC error serialization sends error info without `stack` field → SSR's `buildFakeCallStack(response, errorInfo.stack, ...)` receives `undefined` → crashes on `stack.length` **Additional issues we patched around** (documented in our [PR #132](https://github.com/lucharo/thenewcomputer/pull/132)): - `__vite_rsc_assets_manifest.js` not included in worker output (manual copy needed) - SSR bundle not included in worker output (manual copy + path rewrite from `../../server/ssr/` to `../server/ssr/`) - `react-server` resolve condition dropped from `@vitejs/plugin-rsc` by vinext's `configResolved` override We're moving away from Next.js entirely for our use case, but wanted to document the full findings for other users.
Author
Owner

@camposcristian commented on GitHub (Apr 20, 2026):

Another data point from trying this workaround on a larger Next.js 16 App Router app:

Config applied (per the workaround above):

environments: {
  rsc: {
    resolve: {
      conditions: ["react-server", ...defaultServerConditions],
      noExternal: ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"],
    },
  },
},

Result: the original React.__SERVER_INTERNALS error is resolved, but a secondary issue surfaces from react-dom/server.edge's pre-compiled CJS bundle. It contains literal top-level:

var ReactDOM = require("react-dom"), React = require("react"), ...

These require() calls survive vite's ESM conversion (they're inside a pre-compiled/minified vendor file). The bundle itself is fine-sized (~884 KiB raw for a moderate App Router app + next-international + shadcn), but deploy fails at Workers validation with Uncaught ReferenceError: require is not defined.

Tried workarounds that didn't stick:

  • rollup output.banner injecting a require shim — the shim's own import from "react" stays external (banner is raw text, not processed by the resolver).
  • createRequire(import.meta.url)import.meta.url is undefined during Workers upload validation, so createRequire throws.
  • Aliasing next/dist/compiled/ua-parser-js to a stub for the earlier __dirname variant — works at build time but wrangler's upload-time rebundle re-resolved externals.

The root seems to be that react-dom/server.edge's inline require() calls need to be rewritten at the source level during vinext's build, not via surrounding config. Filing this in case it helps triage alongside the react-server condition fix. Would be happy to share a minimal repro.

Stack: vinext v0.0.7, vite 7.3.2, @cloudflare/vite-plugin (latest), @vitejs/plugin-rsc 0.5.24, Node 22.22.1, Next.js 16.1.6 App Router + next-international + server components.

<!-- gh-comment-id:4277342536 --> @camposcristian commented on GitHub (Apr 20, 2026): Another data point from trying this workaround on a larger Next.js 16 App Router app: **Config applied** (per the workaround above): ```ts environments: { rsc: { resolve: { conditions: ["react-server", ...defaultServerConditions], noExternal: ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"], }, }, }, ``` **Result**: the original `React.__SERVER_INTERNALS` error is resolved, but a secondary issue surfaces from `react-dom/server.edge`'s pre-compiled CJS bundle. It contains literal top-level: ```js var ReactDOM = require("react-dom"), React = require("react"), ... ``` These `require()` calls survive vite's ESM conversion (they're inside a pre-compiled/minified vendor file). The bundle itself is fine-sized (~884 KiB raw for a moderate App Router app + next-international + shadcn), but deploy fails at Workers validation with `Uncaught ReferenceError: require is not defined`. Tried workarounds that didn't stick: - `rollup output.banner` injecting a `require` shim — the shim's own `import from "react"` stays external (banner is raw text, not processed by the resolver). - `createRequire(import.meta.url)` — `import.meta.url` is `undefined` during Workers upload validation, so `createRequire` throws. - Aliasing `next/dist/compiled/ua-parser-js` to a stub for the earlier `__dirname` variant — works at build time but wrangler's upload-time rebundle re-resolved externals. The root seems to be that `react-dom/server.edge`'s inline `require()` calls need to be rewritten at the source level during vinext's build, not via surrounding config. Filing this in case it helps triage alongside the react-server condition fix. Would be happy to share a minimal repro. Stack: vinext v0.0.7, vite 7.3.2, @cloudflare/vite-plugin (latest), @vitejs/plugin-rsc 0.5.24, Node 22.22.1, Next.js 16.1.6 App Router + next-international + server components.
Author
Owner

@quant-risk commented on GitHub (May 4, 2026):

Relato de sucesso com upgrade 0.0.7 -> 0.0.47 + configuracao correta

O que passamos

Trevamos os mesmos erros descritos nesta issue:

  • react-server condition must be enabled no build do cloudflare vite-plugin
  • __SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE undefined no worker runtime
  • Depois de workaround com noExternal + conditions no vite.config.ts, deploy passava mas worker crashava com error 1101
  • Fizemos patches manuais pos-build (copiar manifest, copiar SSR bundle, patchear React internals) que so faziam o deploy passar a validacao -- runtime continuava quebrando ("An error occurred in the Server Components render")

O que realmente resolveu

Upgrade para vinext 0.0.47 (estavamos na 0.0.7) e mudar a config do cloudflare plugin:

Errado (causa os erros desta issue):

plugins: [
  vinext(),
  cloudflare(),
],

Correto (RSC e SSR funcionam sem patching):

plugins: [
  vinext(),
  cloudflare({
    viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] },
  }),
],

Isso faz o vinext gerar o wrangler.json em dist/server/ apontando para o RSC build correto, com client references como registerClientReference em vez de implementacoes reais no servidor. O deploy fica em ~8MB total (worker + assets estaticos em ASSETS binding).

Produzido hoje

Deploy 100% funcional em:

  • Worker: duke-dashboard.henriq-costa91.workers.dev
  • Routes testadas: /, /pricing, /sign-in, /sign-up
  • SSR e hydration funcionando
  • Zero patches manuais necessarios

Recomendacao

Ajustar a documentacao do vinext para destacar que viteEnvironment e obrigatorio para projetos Next.js App Router (com RSC). Quando o parametro nao e fornecido, o cloudflare rebundle perde as fronteiras client/server.

<!-- gh-comment-id:4373966082 --> @quant-risk commented on GitHub (May 4, 2026): **Relato de sucesso com upgrade 0.0.7 -> 0.0.47 + configuracao correta** ### O que passamos Trevamos os mesmos erros descritos nesta issue: - `react-server condition must be enabled` no build do cloudflare vite-plugin - `__SERVER_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE` undefined no worker runtime - Depois de workaround com noExternal + conditions no vite.config.ts, deploy passava mas worker crashava com error 1101 - Fizemos patches manuais pos-build (copiar manifest, copiar SSR bundle, patchear React internals) que so faziam o deploy passar a validacao -- runtime continuava quebrando ("An error occurred in the Server Components render") ### O que realmente resolveu Upgrade para vinext **0.0.47** (estavamos na 0.0.7) e mudar a **config** do cloudflare plugin: **Errado (causa os erros desta issue):** ```ts plugins: [ vinext(), cloudflare(), ], ``` **Correto (RSC e SSR funcionam sem patching):** ```ts plugins: [ vinext(), cloudflare({ viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] }, }), ], ``` Isso faz o vinext gerar o wrangler.json em `dist/server/` apontando para o RSC build correto, com client references como `registerClientReference` em vez de implementacoes reais no servidor. O deploy fica em ~8MB total (worker + assets estaticos em ASSETS binding). ### Produzido hoje Deploy 100% funcional em: - Worker: `duke-dashboard.henriq-costa91.workers.dev` - Routes testadas: `/`, `/pricing`, `/sign-in`, `/sign-up` - SSR e hydration funcionando - Zero patches manuais necessarios ### Recomendacao Ajustar a documentacao do vinext para destacar que `viteEnvironment` e obrigatorio para projetos Next.js App Router (com RSC). Quando o parametro nao e fornecido, o cloudflare rebundle perde as fronteiras client/server.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#123
No description provided.