Which project does this relate to?
Start
Describe the bug
After an HMR invalidation in dev, server function RPC calls (/_serverFn/…) start failing with:
TypeError: (0 , __vite_ssr_import_0__.fetchData) is not a function
where fetchData is a plain named export of an app module (a shared helper imported by several server functions). Once broken, the dev server keeps returning the error until it is restarted.
This looks like the same class of bug as #7285 / #5673, which was closed as fixed by vitejs/vite#22369 (shipped in vite@8.0.12). That fix does resolve the document SSR path — curling the page after an edit renders fine — but the server-function RPC path still races. Notably, #22369 is scoped to re-export chains ("partial-exports race on concurrent imports of in-flight invalidated re-export chains"); here the same race happens with a plain named import, which the fix does not cover. So this likely needs another upstream fix in Vite's module runner, but I'm filing it here first since Start's serverFn RPCs are how it surfaces (and #7285 lived here).
Steps to reproduce
Minimal reproduction (fresh npm install, reproduces reliably):
https://github.com/alvarogomezj/tanstack-start-serverfn-hmr-race
npm install
npm run dev # terminal 1
npm run reproduce # terminal 2 — puppeteer opens the page, appends a comment
# to a server-fn file, and reports the broken RPC payloads
Or manually: open http://localhost:3100/ (a route whose loader calls 8 server functions in parallel, all of which import a shared helper), then append a comment to src/service1.actions.ts and save. Several of the 8 RPC responses now carry the is not a function error and the page renders ERROR rows. Typically 3–5 of 8 calls fail; the count varies because it's a race.
The setup mirrors a real mid-size app (~20 routes, ~40 server-function modules sharing a fetch-wrapper helper) where this happens on every save of a route/server file. The repro's shared helper uses a 300 ms top-level await to stand in for the evaluation time that a real import subtree takes naturally.
Expected behavior
Server functions keep working after HMR, or the affected environment is fully re-evaluated before serving RPCs.
What seems to happen
Instrumenting the SSR module runner's evaluator (runInlinedModule) shows this order after the edit, with the 8 invalidated ?tss-serverfn-split modules re-evaluating concurrently (one per in-flight RPC):
EVAL-START service1.actions.ts?tss-serverfn-split
EVAL-START service2.actions.ts?tss-serverfn-split
...
EVAL-START helper.ts <- shared dep starts evaluating (in-flight)
EVAL-START api1.ts
EVAL-END api1.ts <- finishes WITHOUT awaiting in-flight helper.ts
EVAL-END service1.actions.ts?tss-serverfn-split
...
EVAL-END helper.ts <- shared dep finishes last
api1.ts completes while its dependency helper.ts is still evaluating, so its imported binding is undefined when the RPC handler runs.
I saw the identical failure shape in the real app for imports from @tanstack/react-start/server too ((0 , __vite_ssr_import_1__.getCookie) is not a function), consistent with the re-export-chain variant of the same race.
For triage, things I ruled out in the real app:
Workaround
A dev-only Vite plugin that restarts the dev server whenever a file in the server-function graph actually changes content (SHA-1 dedupe to ignore editor safe-write saves), then sends a full-reload to clients after warming the new server with a self-fetch (the automatic client reload otherwise races the server warm-up and renders the error into the page). Reliable, but a full restart per server-file edit is a heavy price, and pure Fast Refresh flows lose out.
Platform
- OS: macOS (Darwin 25.5.0)
- Node: 24.14.1
- Browser: Chrome (also headless via puppeteer in the repro script)
Package versions
| Package |
Version |
| @tanstack/react-start |
1.168.27 |
| @tanstack/react-router |
1.170.17 |
| vite |
8.1.3 |
| @vitejs/plugin-react |
6.0.3 |
| react / react-dom |
19.2.4 |
Which project does this relate to?
Start
Describe the bug
After an HMR invalidation in dev, server function RPC calls (
/_serverFn/…) start failing with:where
fetchDatais a plain named export of an app module (a shared helper imported by several server functions). Once broken, the dev server keeps returning the error until it is restarted.This looks like the same class of bug as #7285 / #5673, which was closed as fixed by vitejs/vite#22369 (shipped in
vite@8.0.12). That fix does resolve the document SSR path —curling the page after an edit renders fine — but the server-function RPC path still races. Notably, #22369 is scoped to re-export chains ("partial-exports race on concurrent imports of in-flight invalidated re-export chains"); here the same race happens with a plain named import, which the fix does not cover. So this likely needs another upstream fix in Vite's module runner, but I'm filing it here first since Start's serverFn RPCs are how it surfaces (and #7285 lived here).Steps to reproduce
Minimal reproduction (fresh
npm install, reproduces reliably):https://github.com/alvarogomezj/tanstack-start-serverfn-hmr-race
Or manually: open
http://localhost:3100/(a route whose loader calls 8 server functions in parallel, all of which import a shared helper), then append a comment tosrc/service1.actions.tsand save. Several of the 8 RPC responses now carry theis not a functionerror and the page rendersERRORrows. Typically 3–5 of 8 calls fail; the count varies because it's a race.The setup mirrors a real mid-size app (~20 routes, ~40 server-function modules sharing a fetch-wrapper helper) where this happens on every save of a route/server file. The repro's shared helper uses a 300 ms top-level
awaitto stand in for the evaluation time that a real import subtree takes naturally.Expected behavior
Server functions keep working after HMR, or the affected environment is fully re-evaluated before serving RPCs.
What seems to happen
Instrumenting the SSR module runner's evaluator (
runInlinedModule) shows this order after the edit, with the 8 invalidated?tss-serverfn-splitmodules re-evaluating concurrently (one per in-flight RPC):api1.tscompletes while its dependencyhelper.tsis still evaluating, so its imported binding isundefinedwhen the RPC handler runs.I saw the identical failure shape in the real app for imports from
@tanstack/react-start/servertoo ((0 , __vite_ssr_import_1__.getCookie) is not a function), consistent with the re-export-chain variant of the same race.For triage, things I ruled out in the real app:
nitroVite plugin (the repro repo doesn't use it).evaluatedModules.clear()fix suggested in Server HMR broken: ModuleRunner evaluatedModules cache not cleared on reload nitrojs/nitro#4020 locally did not help.vite@8.1.3(includes the #22369 fix),@tanstack/react-start@1.168.27,@tanstack/react-router@1.170.17, React 19.2.4, Node 24, macOS.Workaround
A dev-only Vite plugin that restarts the dev server whenever a file in the server-function graph actually changes content (SHA-1 dedupe to ignore editor safe-write saves), then sends a
full-reloadto clients after warming the new server with a self-fetch (the automatic client reload otherwise races the server warm-up and renders the error into the page). Reliable, but a full restart per server-file edit is a heavy price, and pure Fast Refresh flows lose out.Platform
Package versions