cloudflare / cloudflare/vinext

@vitejs/plugin-rsc serverHandler middleware crashes on environment.runner.import when nitro/vite plugin is present

Open
#853 2 comments 1 reaction 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.