cloudflare / cloudflare/vinext
@vitejs/plugin-rsc serverHandler middleware crashes on environment.runner.import when nitro/vite plugin is present
- Dominant language
- TypeScript
- Stars
- 8.8k
- Forks
- 406
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 120
Description
## Summary
When `vinext` is used together with `nitro/vite` (as shown in vinext's own setup for deployment), every request to the dev server returns a 500 with:
```
[vite] Internal server error: Cannot read properties of undefined (reading 'import')
at …/@vitejs/plugin-rsc/dist/plugin-DMfc_Eqq.js:784:76
```
## Versions
- `vinext@0.0.41`
- `@vitejs/plugin-rsc@0.5.24`
- `vite@8.0.8`
- `nitro` → `nitro-nightly@3.0.1-20260311-140511-1d05f55f`
- Node `v22.22.1`, pnpm `10.28.2`
## Reproduction
Minimal `vite.config.ts`:
```ts
import vinext from 'vinext';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'vite';
import { nitro } from 'nitro/vite';
export default defineConfig({
plugins: [tailwindcss(), vinext(), nitro({ noExternals: ['tslib'] })],
});
```
App with a Next.js App Router layout. `pnpm dev` → any request to `/` fails.
## Logs
```
1:50:19 PM [vite] Internal server error: Cannot read properties of undefined (reading 'import')
at file:///…/@vitejs+plugin-rsc@0.5.24_…/node_modules/@vitejs/plugin-rsc/dist/plugin-DMfc_Eqq.js:784:76
GET / 500 in 3ms
GET /warehouse-requests 500 in 2ms
GET /payment-requests?priority=urgent 500 in 2ms
GET /payment-receipts 500 in 3ms
GET /kanban 500 in 2ms
```
The same error repeats for every route.
## Root cause (as far as I can trace)
`@vitejs/plugin-rsc@0.5.24` registers a `configureServer` middleware that does:
```js
// @vitejs/plugin-rsc/dist/plugin-DMfc_Eqq.js around L784
const environment = server.environments[options.environmentName]; // "rsc"
const resolved = await environment.pluginContainer.resolveId(source);
const fetchHandler = getFetchHandlerExport(
await environment.runner.import(resolved.id) // ← crashes here
);
```
This path assumes `environment` is a `RunnableDevEnvironment`. When `nitro/vite` is in the plugin chain, nitro's env factory (`createFetchableDevEnvironment` in `nitro-nightly/dist/vite.mjs`) replaces some environments with a `FetchableDevEnvironment` subclass that does **not** have a `.runner` — it exposes `fetchModule()` / `devServer.fetch` instead. So `environment.runner` is `undefined` and the next property access (`.import`) throws.
Elsewhere in plugin-rsc (in the `import.meta.viteRsc.import` path), the code *does* guard with `vite.isRunnableDevEnvironment(environment)` before accessing `.runner`. The `configureServer` middleware and `configurePreviewServer` paths don't have that guard.
Interestingly, vinext itself already documents this exact situation in `vinext/dist/server/dev-module-runner.js` (comment preserved from source):
> When `@cloudflare/vite-plugin` is present it registers its own Vite environments (e.g. `"rsc"`, `"ssr"`) that are *not* `RunnableDevEnvironment` instances. Calling `ssrLoadModule()` in that context crashes…
…and provides a `fetchModule()`-based workaround, but that workaround isn't wired into plugin-rsc's own middleware.
## Impact
With this combination — the combination vinext's own docs recommend for nitro-based deployments — **every request in dev returns 500**. The app is completely unusable for local development.
## Possible fixes
1. In `@vitejs/plugin-rsc` `configureServer`/`configurePreviewServer`, detect non-runnable environments and fall back to `environment.fetchModule()` (mirroring the `vite.isRunnableDevEnvironment` check already present elsewhere in the same file).
2. In vinext, pass `serverHandler: false` to the auto-registered `rsc()` plugin when a nitro plugin is detected, and replace it with vinext's own `fetchModule`-based handler.
Option 2 keeps the fix scoped to this integration and doesn't require a plugin-rsc release.
## Workaround
Downgrading `vinext` to `^0.0.28` (which pulls `@vitejs/plugin-rsc@^0.5.19`, a version without this direct `.runner.import` call in its middleware) restores dev.
Contributor guide
Research direction
Start with vinext/dist/server/dev-module-runner.js and the plugin registration that auto-registers rsc(), then reproduce the failure with the minimal vite.config.ts and the listed package versions. Trace how the nitro/vite environment reaches @vitejs/plugin-rsc's configureServer middleware and determine whether vinext's fetchModule-based workaround can handle it. Done means the documented nitro setup serves every listed route without a 500.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript, vite
- Domain
- developer-experience, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100