Summary
On Vite 8.2.x (which pulls Rolldown 1.2.2+), the Nitro pass emits an SSR
service chunk that re-exports a namespace binding it never declares. Node
throws while compiling the module, so every single request 500s — including
the login page — with no failure at build time.
There is a second, opposite failure on Rolldown 1.1.2 … 1.2.1, where the SPA
prerender step dies with TypeError: createServerFn is not a function. The two
are mutually exclusive across versions, so no single Rolldown version is green
for both an SSR app and a prerendered SPA app in the same monorepo.
I believe the root cause is in Rolldown chunking, not in Start — filing here
because the failure is only reachable through the Start + Nitro pipeline, and
because the second symptom is one of your own exports. Happy to move this to
rolldown/rolldown if you prefer.
Symptom 1 — ssr_exports is not defined (Vite 8.2.x / Rolldown 1.2.2+)
Runtime error on every request:
SyntaxError: Export 'ssr_exports' is not defined in module
at compileSourceTextModule (node:internal/modules/esm/utils:318:16)
at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:89:18)
…
status: 500,
unhandled: true
The offending chunk, .output/server/_ssr/ssr2.mjs, ends with:
export { ssr_exports as a, getRequestHeader as c, createServerEntry,
requestHandler as d, server_default as default, /* … */ };
ssr_exports is neither declared in this module nor imported by it — the
only other __exportAll namespace in the same file (server_exports) is
declared, two lines above:
var server_exports = /* @__PURE__ */ __exportAll({ getResponseHeaders: () => getResponseHeaders });
//#endregion
export { ssr_exports as a, /* … */ }; // ← ssr_exports never declared
This is not a dead export. It is the namespace Nitro loads as the SSR service:
// .output/server/_chunks/ssr-renderer.mjs
var viteServices = {
["ssr"]: lazyService(() => import("../_ssr/ssr2.mjs").then((n) => n.a))
};
n.a is exactly ssr_exports, so the SSR renderer can never load.
Two more details that seem relevant to diagnosing it:
-
The intermediate Vite output is clean. I grepped
node_modules/.nitro/vite/services/ssr/ (the index.js entry plus its
assets/): zero occurrences of ssr_exports. Other namespaces
(server_exports, request_helpers_exports) are declared normally there.
The undeclared binding is introduced by the Nitro re-bundle pass.
-
The service entry gets split into two mutually-importing chunks.
A working app emits a single _ssr/ssr.mjs that declares ssr_exports; the
broken one emits _ssr/ssr.mjs and _ssr/ssr2.mjs which import from each
other:
// ssr.mjs
import { d as requestHandler, u as getResponse } from "./ssr2.mjs";
// ssr2.mjs
import { a as getServerFnById, i as createServerFn, /* … */ } from "./ssr.mjs";
Rolldown 1.2.2's release notes list "support inlining of shared deps in
dynamic entries" — and the SSR service is loaded through exactly such a dynamic
entry, which is my best guess at the trigger.
Symptom 2 — createServerFn is not a function (Rolldown 1.1.2 … 1.2.1)
Pinning Rolldown down to escape symptom 1 hits the known chunking regression
instead. Build exits 0, but prerender produces nothing:
[prerender] Prerendered 0 pages:
[unhandledRejection] Error: Failed to fetch /: Internal Server Error
cause: TypeError: createServerFn is not a function
at .output/server/_ssr/ssr.mjs:232:28
This matches rolldown#9993.
Note that issue is closed (2026-07-05) but a commenter confirmed five days
later that it still reproduces on rolldown@1.1.5 — "same failure class, but a
graph shape the cycle check in #10101 still misses". Both symptoms sit under the
rolldown#10008 chunking
umbrella (category correctness/cross-chunk-init-binding).
Measured version matrix
One SSR app (prerender: { enabled: false }, per-route SSR) and two prerendered
SPA apps, same monorepo, full vite build per row:
| rolldown |
SSR app |
prerendered SPA app |
| 1.1.2 / 1.1.5 / 1.2.0 / 1.2.1 |
✅ |
❌ createServerFn is not a function |
| 1.2.2 / 1.2.3 |
❌ ssr_exports |
✅ |
| 1.1.0 + vite 8.1.5 |
✅ |
✅ |
Things that did not help, in case they save someone time:
- Aligning versions so Vite and Nitro share one Rolldown (
overrides: { rolldown: 1.2.3 }).
Note both are present by default: Vite 8.2.1 pulls rolldown@1.2.3 while
nitro@3.0.260610-beta depends on rolldown@^1.1.0.
- Downgrading Nitro to
3.0.260522-beta.
- Bumping
@tanstack/start-plugin-core to 1.171.33.
- Disabling the React Compiler babel pass.
Reproduction
I do not have a minimal isolated repro — the failure only appears at real
module-graph size (the app is ~300 server modules; a toy Start app does not
trigger it). Same caveat as rolldown#10228, where the reporter also could not
get below ~500 modules. What reproduces reliably in our app:
pnpm --filter <ssr-app> build # exits 0
node --check .output/server/_ssr/ssr2.mjs
# SyntaxError: Export 'ssr_exports' is not defined in module
I'm glad to run experiments against a patched build or dump any intermediate
artifact if that helps narrow it.
The part that worries me most
vite build exits 0 on a bundle that cannot serve a single request. This is
not a syntax error in the file, so nothing surfaces until Node compiles the
module at request time. Our CI was green, the deploy succeeded, and production
returned 500 on every route until a human noticed.
We now gate builds with a node --check sweep over every emitted server module
(~1.7s for 988 modules), which catches this class immediately:
ESM output check passed: 183 server modules link cleanly.
Something equivalent inside Start's build (or a documented recommendation) would
have turned a production outage into a failed build.
Environment
@tanstack/react-start 1.168.40
@tanstack/start-plugin-core 1.171.31 (also tested 1.171.33)
@tanstack/react-router 1.170.23
nitro 3.0.260610-beta (also tested 3.0.260522-beta)
vite 8.2.1 (broken) / 8.1.5 (works)
rolldown see matrix above
react / react-dom 19.2.8
node 24.15.0
pnpm 11.4.0
preset node-server (same failure on the Vercel preset)
Ask
- Is pinning
vite@8.1.5 + rolldown@1.1.0 the right stopgap for Start users
today, or is there a supported combination on Vite 8.2 that I missed?
- Would you consider carrying a known-good Rolldown range for Start, given that
neither side of the matrix is green for mixed SSR + prerender monorepos?
- Is a link-validity check on emitted server chunks something Start would take?
Happy to open a PR.
Summary
On Vite
8.2.x(which pulls Rolldown1.2.2+), the Nitro pass emits an SSRservice chunk that re-exports a namespace binding it never declares. Node
throws while compiling the module, so every single request 500s — including
the login page — with no failure at build time.
There is a second, opposite failure on Rolldown
1.1.2 … 1.2.1, where the SPAprerender step dies with
TypeError: createServerFn is not a function. The twoare mutually exclusive across versions, so no single Rolldown version is green
for both an SSR app and a prerendered SPA app in the same monorepo.
I believe the root cause is in Rolldown chunking, not in Start — filing here
because the failure is only reachable through the Start + Nitro pipeline, and
because the second symptom is one of your own exports. Happy to move this to
rolldown/rolldownif you prefer.Symptom 1 —
ssr_exportsis not defined (Vite 8.2.x / Rolldown 1.2.2+)Runtime error on every request:
The offending chunk,
.output/server/_ssr/ssr2.mjs, ends with:ssr_exportsis neither declared in this module nor imported by it — theonly other
__exportAllnamespace in the same file (server_exports) isdeclared, two lines above:
This is not a dead export. It is the namespace Nitro loads as the SSR service:
n.ais exactlyssr_exports, so the SSR renderer can never load.Two more details that seem relevant to diagnosing it:
The intermediate Vite output is clean. I grepped
node_modules/.nitro/vite/services/ssr/(theindex.jsentry plus itsassets/): zero occurrences ofssr_exports. Other namespaces(
server_exports,request_helpers_exports) are declared normally there.The undeclared binding is introduced by the Nitro re-bundle pass.
The service entry gets split into two mutually-importing chunks.
A working app emits a single
_ssr/ssr.mjsthat declaresssr_exports; thebroken one emits
_ssr/ssr.mjsand_ssr/ssr2.mjswhich import from eachother:
Rolldown
1.2.2's release notes list "support inlining of shared deps indynamic entries" — and the SSR service is loaded through exactly such a dynamic
entry, which is my best guess at the trigger.
Symptom 2 —
createServerFn is not a function(Rolldown 1.1.2 … 1.2.1)Pinning Rolldown down to escape symptom 1 hits the known chunking regression
instead. Build exits 0, but prerender produces nothing:
This matches rolldown#9993.
Note that issue is closed (2026-07-05) but a commenter confirmed five days
later that it still reproduces on
rolldown@1.1.5— "same failure class, but agraph shape the cycle check in #10101 still misses". Both symptoms sit under the
rolldown#10008 chunking
umbrella (category correctness/cross-chunk-init-binding).
Measured version matrix
One SSR app (
prerender: { enabled: false }, per-route SSR) and two prerenderedSPA apps, same monorepo, full
vite buildper row:createServerFn is not a functionssr_exportsThings that did not help, in case they save someone time:
overrides: { rolldown: 1.2.3 }).Note both are present by default: Vite
8.2.1pullsrolldown@1.2.3whilenitro@3.0.260610-betadepends onrolldown@^1.1.0.3.0.260522-beta.@tanstack/start-plugin-coreto1.171.33.Reproduction
I do not have a minimal isolated repro — the failure only appears at real
module-graph size (the app is ~300 server modules; a toy Start app does not
trigger it). Same caveat as rolldown#10228, where the reporter also could not
get below ~500 modules. What reproduces reliably in our app:
I'm glad to run experiments against a patched build or dump any intermediate
artifact if that helps narrow it.
The part that worries me most
vite buildexits 0 on a bundle that cannot serve a single request. This isnot a syntax error in the file, so nothing surfaces until Node compiles the
module at request time. Our CI was green, the deploy succeeded, and production
returned 500 on every route until a human noticed.
We now gate builds with a
node --checksweep over every emitted server module(~1.7s for 988 modules), which catches this class immediately:
Something equivalent inside Start's build (or a documented recommendation) would
have turned a production outage into a failed build.
Environment
Ask
vite@8.1.5+rolldown@1.1.0the right stopgap for Start userstoday, or is there a supported combination on Vite 8.2 that I missed?
neither side of the matrix is green for mixed SSR + prerender monorepos?
Happy to open a PR.