Repository navigation
Turnkey SSR dev fails with non-runnable Cloudflare ssr environment #281
Description
Summary
Turnkey SSR development fails when the ssr environment is supplied by @cloudflare/vite-plugin because vite-plugin-solid calls the legacy server.ssrLoadModule() API, which only works with a RunnableDevEnvironment.
Cloudflare recommends assigning its Worker to the framework's SSR environment with viteEnvironment: { name: 'ssr' }. Its environment runs modules in workerd and is intentionally not runnable in the Vite server process.
Production builds and previews work; the failure is specific to development.
Versions
vite-plugin-solid:3.0.0-next.15vite:8.1.5solid-js:2.0.0-beta.23@solidjs/web:2.0.0-beta.23@cloudflare/vite-plugin:1.46.0wrangler:4.113.0
Minimal reproduction
vite.config.ts:
import { cloudflare } from '@cloudflare/vite-plugin'
import { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'
export default defineConfig({
plugins: [
cloudflare({ viteEnvironment: { name: 'ssr' } }),
solid({ ssr: {}, serverFunctions: true }),
],
})wrangler.jsonc:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "solid-cloudflare",
"main": "./src/worker.ts",
"compatibility_date": "2026-07-22",
"compatibility_flags": ["nodejs_compat"],
"assets": {
"directory": "./dist/client",
"binding": "ASSETS"
}
}src/worker.ts:
import { handleRequest } from 'virtual:solid-ssr-handler'
export default {
fetch(request: Request) {
return handleRequest(request)
},
}Add a basic src/App.tsx, run pnpm dev, and request / with Accept: text/html.
Actual behavior
Internal server error: ssrLoadModule requires the 'ssr' environment to be a runnable environment.
at ssrLoadModule (.../vite/dist/node/chunks/node.js)
at Proxy.ssrLoadModule (.../vite/dist/node/chunks/node.js)
at .../vite-plugin-solid/dist/esm/index.mjs
The call originates from the turnkey middleware in src/ssr/index.ts, which does:
const handler = await server.ssrLoadModule(HANDLER_ID)The server-functions development middleware in src/server-functions/index.ts also calls server.ssrLoadModule() for both the referenced module and the virtual handler, so it appears to have the same environment limitation.
Expected behavior
Turnkey SSR should either dispatch through the environment's supported communication mechanism or defer to the runtime provider's middleware instead of assuming that ssr is runnable in the Vite server process.
The existing Worker entry already imports the adapter-agnostic virtual:solid-ssr-handler, so Cloudflare can execute the handler inside workerd if Solid's Node-oriented middleware delegates the request.
Verified production behavior
pnpm buildsucceeds and emits the Worker/server and client bundles.pnpm previewreturns200with server-rendered HTML from the Worker runtime.
Current workaround
Keeping the Cloudflare Worker out of the ssr environment during development leaves Vite's default runnable SSR environment available to Solid, while preserving the production environment merge:
export default defineConfig(({ command }) => ({
plugins: [
cloudflare({
viteEnvironment: command === 'build' ? { name: 'ssr' } : undefined,
}),
solid({ ssr: {}, serverFunctions: true }),
],
}))This avoids the exception but means development does not follow the intended single Cloudflare SSR environment setup.
Possible direction
Use Vite's Environment API instead of server.ssrLoadModule():
- For
isRunnableDevEnvironment(environment), load virtual handlers withenvironment.runner.import(). - For a
FetchableDevEnvironment, dispatch the web request withenvironment.dispatchFetch(). - For a provider-owned raw environment such as Cloudflare's current environment, call
next()so the provider can route the request to its runtime. - Apply equivalent capability handling to the server-functions development middleware and ensure the generated Worker handler can serve server-function requests in development.
An explicit turnkey option such as ssr.devMiddleware: false could also provide an adapter-controlled escape hatch, although automatic capability detection would avoid Cloudflare-specific configuration.
Activity
ryansolid commentedon Jul 29, 2026
Fixed on
nextby the approach from #286 — thanks @brenelz for both the detailed report and the implementation. Two mechanisms landed:ssrenvironment isn't runnable — exactly theviteEnvironment: { name: 'ssr' }Cloudflare setup here — the plugin detects it and stands its dev middlewares down (turnkey SSR serving and the server-function endpoint both) instead of callingssrLoadModule. Requests fall through to the provider, and the generatedvirtual:solid-ssr-handlerself-serves when workerd imports it: dev entry CSS travels through an HMR-tracked virtual styles module and server functions compose into the handler, sohandleRequest(request)is the whole dev contract, same as production. Zero config.ssr.external: true. The umbrella switch for integrations that own the whole server side — it additionally skips turnkey's server-build wiring, and covers providers that own a differently named environment that detection can't see.The narrow, endpoint-only knob
serverFunctions.devMiddleware: falseremains for non-turnkey setups; the README onnextnow documents how the three options relate.Ships in the next
3.0.0-nextrelease.