[GH-ISSUE #1008] vinext shims with 'use client' aren't registered in clientReferenceMetaMap, causing @vitejs/plugin-rsc to crash. #221

Closed
opened 2026-05-06 12:38:19 +02:00 by BreizhHardware · 0 comments

Originally created by @eashish93 on GitHub (May 2, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/1008

Versions

  • vinext: 0.0.45
  • @vitejs/plugin-rsc: 0.5.25
  • vite: 8.0.10
  • Node 24 / Bun 1.3.11
  • macOS 15 (Apple Silicon)

Symptom

Navigating to an App Router route that transitively imports any vinext/shims/* file with 'use client' (e.g. via next/link, next/navigation, next/image, next/form, next/script) crashes the dev server with:

[vite] Internal server error: Cannot read properties of undefined (reading 'exportNames')
    at LoadPluginContext.handler (.../@vitejs/plugin-rsc/dist/plugin-BhzHKRFo.js:1395:110)
    at EnvironmentPluginContainer.load (.../vite/dist/node/chunks/node.js:30122:56)
    at async loadAndTransform (.../vite/dist/node/chunks/node.js:24465:21)

The route renders blank, with the error only visible in the wrangler/vite terminal. Other routes that don't pull a 'use client' shim through the server-component graph load fine — which made it look route-specific and very confusing to diagnose.

Root cause

@vitejs/plugin-rsc generates a \0virtual:vite-rsc/client-package-proxy/<source> virtual module for each 'use client' node_modules package transitively imported by a server component. The load handler then looks up the matching entry in clientReferenceMetaMap to know which exports to re-export.

The following vinext shim files all start with 'use client':

  • vinext/dist/shims/layout-segment-context.js
  • vinext/dist/shims/slot.js
  • vinext/dist/shims/link.js
  • vinext/dist/shims/navigation.js
  • vinext/dist/shims/form.js
  • vinext/dist/shims/script.js
  • vinext/dist/shims/error-boundary.js
  • vinext/dist/shims/client-hook-error.js

When server code imports e.g. next/link (resolved by vinext to vinext/shims/link), plugin-rsc's resolveId hook records the package source. Vite's depsOptimizer then bundles vinext/shims/* into a consolidated chunk that includes unrelated vinext server code. The path of the optimized chunk drifts from what plugin-rsc scanned, so its clientReferenceMetaMap lookup misses, the load handler hits undefined.exportNames, and the entire dev server bundle for that route crashes.

After patching plugin-rsc with a defensive guard, the warning identified the actual offenders:

[vite-rsc PATCH] no clientReferenceMetaMap entry for "vinext/shims/layout-segment-context" — falling back to wildcard re-export
[vite-rsc PATCH] no clientReferenceMetaMap entry for "vinext/shims/slot" — falling back to wildcard re-export

Reproduction shape

(I don't have a hosted repro since this is a private app, but the recipe is straightforward.)

A vinext App Router project where:

  1. The route's server component renders a 'use client' component (e.g. a chat UI)
  2. That client component imports next/link and at least one other 'use client' node_modules package (e.g. sonner, swr)
  3. Vite's depsOptimizer runs in dev (default)

The route bundle crashes on first request with the exportNames error.

Workaround we shipped

Patched @vitejs/plugin-rsc/dist/plugin-BhzHKRFo.js line 1395 with a defensive guard that warns + falls back to export * when the meta lookup misses, persisted via bun patch:

const meta = Object.values(manager.clientReferenceMetaMap).find((v) => v.packageSource === source);
if (!meta) {
  console.warn(`[vite-rsc PATCH] no clientReferenceMetaMap entry for "${source}" — falling back to wildcard re-export`);
  return `export * from ${JSON.stringify(source)};\n`;
}
return `export {${meta.exportNames.join(",")}} from ${JSON.stringify(source)};\n`;

This unblocks the dev server, but export * doesn't preserve named-export semantics perfectly for every package. The right fix lives in vinext.

Proposed fix (vinext side)

Two viable directions, either would resolve this:

  1. Pre-register shims with plugin-rsc. When vinext sets up the rsc() plugin, also configure plugin-rsc to know about all vinext/shims/* files that ship 'use client'. They're a known fixed list at vinext build time.
  2. Force-include shims in optimizeDeps.exclude. Have vinext inject optimizeDeps.exclude: ['vinext/shims/...'] (or per-shim entries) automatically so Vite doesn't bundle them into a consolidated chunk in the first place. Users hit this without knowing they need to do it themselves, and the per-shim list will keep growing as vinext adds shims.

Happy to PR option 2 if it'd help — needs guidance on the right place in dist/index.js to inject the config.

Adjacent observation (worth a separate issue?)

@vitejs/plugin-rsc's assumption at line 1395 that find() always succeeds is itself a defensive-coding gap. Frameworks built on top of plugin-rsc (vinext, Waku, custom) all stand to crash the same way if any client package isn't registered. The handler should fall back gracefully + log instead of throwing. Plugin-rsc's source already has a warnInoncistentClientOptimization() helper that recommends optimizeDeps.exclude — it should be paired with a non-fatal load-handler fallback.

(Filing this with vinext first since the actionable fix is here. Plugin-rsc maintainers want a CodeSandbox repro for upstream issues, which is harder to construct in isolation than as a vinext-on-real-app problem.)

Originally created by @eashish93 on GitHub (May 2, 2026). Original GitHub issue: https://github.com/cloudflare/vinext/issues/1008 ## Versions - `vinext`: 0.0.45 - `@vitejs/plugin-rsc`: 0.5.25 - `vite`: 8.0.10 - Node 24 / Bun 1.3.11 - macOS 15 (Apple Silicon) ## Symptom Navigating to an App Router route that transitively imports any `vinext/shims/*` file with `'use client'` (e.g. via `next/link`, `next/navigation`, `next/image`, `next/form`, `next/script`) crashes the dev server with: ```ts [vite] Internal server error: Cannot read properties of undefined (reading 'exportNames') at LoadPluginContext.handler (.../@vitejs/plugin-rsc/dist/plugin-BhzHKRFo.js:1395:110) at EnvironmentPluginContainer.load (.../vite/dist/node/chunks/node.js:30122:56) at async loadAndTransform (.../vite/dist/node/chunks/node.js:24465:21) ``` The route renders blank, with the error only visible in the wrangler/vite terminal. Other routes that don't pull a `'use client'` shim through the server-component graph load fine — which made it look route-specific and very confusing to diagnose. ## Root cause `@vitejs/plugin-rsc` generates a `\0virtual:vite-rsc/client-package-proxy/<source>` virtual module for each `'use client'` node_modules package transitively imported by a server component. The `load` handler then looks up the matching entry in `clientReferenceMetaMap` to know which exports to re-export. The following vinext shim files all start with `'use client'`: - `vinext/dist/shims/layout-segment-context.js` - `vinext/dist/shims/slot.js` - `vinext/dist/shims/link.js` - `vinext/dist/shims/navigation.js` - `vinext/dist/shims/form.js` - `vinext/dist/shims/script.js` - `vinext/dist/shims/error-boundary.js` - `vinext/dist/shims/client-hook-error.js` When server code imports e.g. `next/link` (resolved by vinext to `vinext/shims/link`), plugin-rsc's `resolveId` hook records the package source. Vite's `depsOptimizer` then bundles `vinext/shims/*` into a consolidated chunk that includes unrelated vinext server code. The path of the optimized chunk drifts from what plugin-rsc scanned, so its `clientReferenceMetaMap` lookup misses, the load handler hits `undefined.exportNames`, and the entire dev server bundle for that route crashes. After patching plugin-rsc with a defensive guard, the warning identified the actual offenders: ``` [vite-rsc PATCH] no clientReferenceMetaMap entry for "vinext/shims/layout-segment-context" — falling back to wildcard re-export [vite-rsc PATCH] no clientReferenceMetaMap entry for "vinext/shims/slot" — falling back to wildcard re-export ``` ## Reproduction shape (I don't have a hosted repro since this is a private app, but the recipe is straightforward.) A vinext App Router project where: 1. The route's server component renders a `'use client'` component (e.g. a chat UI) 2. That client component imports `next/link` and at least one other `'use client'` node_modules package (e.g. `sonner`, `swr`) 3. Vite's `depsOptimizer` runs in dev (default) The route bundle crashes on first request with the `exportNames` error. ## Workaround we shipped Patched `@vitejs/plugin-rsc/dist/plugin-BhzHKRFo.js` line 1395 with a defensive guard that warns + falls back to `export *` when the meta lookup misses, persisted via `bun patch`: ```js const meta = Object.values(manager.clientReferenceMetaMap).find((v) => v.packageSource === source); if (!meta) { console.warn(`[vite-rsc PATCH] no clientReferenceMetaMap entry for "${source}" — falling back to wildcard re-export`); return `export * from ${JSON.stringify(source)};\n`; } return `export {${meta.exportNames.join(",")}} from ${JSON.stringify(source)};\n`; ``` This unblocks the dev server, but `export *` doesn't preserve named-export semantics perfectly for every package. The right fix lives in vinext. ## Proposed fix (vinext side) Two viable directions, either would resolve this: 1. **Pre-register shims with plugin-rsc.** When vinext sets up the `rsc()` plugin, also configure plugin-rsc to know about all `vinext/shims/*` files that ship `'use client'`. They're a known fixed list at vinext build time. 2. **Force-include shims in `optimizeDeps.exclude`.** Have vinext inject `optimizeDeps.exclude: ['vinext/shims/...']` (or per-shim entries) automatically so Vite doesn't bundle them into a consolidated chunk in the first place. Users hit this without knowing they need to do it themselves, and the per-shim list will keep growing as vinext adds shims. Happy to PR option 2 if it'd help — needs guidance on the right place in `dist/index.js` to inject the config. ## Adjacent observation (worth a separate issue?) `@vitejs/plugin-rsc`'s assumption at line 1395 that `find()` always succeeds is itself a defensive-coding gap. Frameworks built on top of plugin-rsc (vinext, Waku, custom) all stand to crash the same way if any client package isn't registered. The handler should fall back gracefully + log instead of throwing. Plugin-rsc's source already has a `warnInoncistentClientOptimization()` helper that recommends `optimizeDeps.exclude` — it should be paired with a non-fatal load-handler fallback. (Filing this with vinext first since the actionable fix is here. Plugin-rsc maintainers want a CodeSandbox repro for upstream issues, which is harder to construct in isolation than as a vinext-on-real-app problem.)
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#221
No description provided.