Skip to content

Turnkey SSR dev fails with non-runnable Cloudflare ssr environment #281

Description

@brenelz

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.15
  • vite: 8.1.5
  • solid-js: 2.0.0-beta.23
  • @solidjs/web: 2.0.0-beta.23
  • @cloudflare/vite-plugin: 1.46.0
  • wrangler: 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 build succeeds and emits the Worker/server and client bundles.
  • pnpm preview returns 200 with 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 with environment.runner.import().
  • For a FetchableDevEnvironment, dispatch the web request with environment.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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions