TanStack / TanStack/router

createMiddleware is not a function on cold-start SSR in vite dev (not HMR; @tanstack/react-start@1.168.9, Vite 7.3.3)

Open
#7,459 8 comments 8 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

needs-upstream-fix
Dominant language
TypeScript
Stars
15.1k
Forks
1.9k
Avg merge
1d 20h
Merged PRs (30d)
143

Description

Summary

@tanstack/react-start@1.168.9 throws (0, __vite_ssr_import_N__.createMiddleware) is not a function on the first SSR request after vite dev boots. No HMR involved — error fires on cold start, before any file edit.

Distinct from #7285 (which fails on HMR re-evaluation only, with cold start succeeding). Same root mechanism (Vite SSR module runner mishandling export * re-exports through TanStack's package facade), different surfacing.

npm run build + npm run start (prod SSR via Rollup) work correctly — Rollup statically resolves the export * chain, so the bug only surfaces in vite dev's on-demand SSR module evaluation.

Versions

  • @tanstack/react-start: ^1.168.9
  • @tanstack/react-router: ^1.170.6
  • @tanstack/router-plugin: ^1.168.9
  • vite: ^7.3.3
  • nitro: ^3.0.260429-beta
  • Node: v24.5.0
  • OS: macOS (darwin 25.3.0)

Reproduction

Minimal user code triggering the bug:

// src/middlewares/auth.ts
import { createMiddleware } from "@tanstack/react-start"

export const ensureAuthenticatedMiddleware = createMiddleware().server(async ({ next }) => {
  return next({ context: {} })
})

Any file in that imports this middleware will trigger the error on first SSR request.

Stack trace

TypeError: (0 , __vite_ssr_import_2__.createMiddleware) is not a function
    at /<repo>/src/middlewares/auth.ts:8:46
    at ESModulesEvaluator.runInlinedModule (file:///<repo>/node_modules/vite/dist/node/module-runner.js:913:3)
    at ModuleRunner.directRequest (file:///<repo>/node_modules/vite/dist/node/module-runner.js:1146:59)
    at ModuleRunner.cachedRequest (file:///<repo>/node_modules/vite/dist/node/module-runner.js:1053:73)
    at /<repo>/src/routes/_authenticated/-serverfns/auth.ts:1:1
    ...

Diagnosis

createMiddleware is not a direct export of @tanstack/react-start anymore — it's re-exported transitively:

// @tanstack/react-start/dist/esm/index.js
import { useServerFn } from "./useServerFn.js";
import { createServerFn } from "@tanstack/start-client-core";
import { Hydrate } from "@tanstack/react-start-client";
export * from "@tanstack/start-client-core";  // ← createMiddleware lives here
export { Hydrate, createServerFn, useServerFn };

Plain Node ESM resolves the chain correctly:

\$ node -e "import('@tanstack/react-start').then(m => console.log(typeof m.createMiddleware))"
function

But Vite's SSR ESModulesEvaluator returns undefined for .createMiddleware on the namespace it loaded for @tanstack/react-start. The export * re-export from @tanstack/start-client-core is not being walked through to the user module.

Workaround (confirmed working)

Adding both packages to ssr.optimizeDeps.include flattens the re-export chain at optimize time (mirroring Rollup's behavior in prod):

// vite.config.ts
export default defineConfig({
  plugins: [/* ... */],
  ssr: {
    optimizeDeps: {
      include: ["@tanstack/react-start", "@tanstack/start-client-core"],
    },
  },
})

Cold start succeeds with this in place; full reinstall + cache clear without it does not help.

What I've ruled out

  • ✅ Fresh rm -rf node_modules && npm install
  • ✅ Cleared node_modules/.vite, .vite, .nitro, .output
  • ✅ Bumped Vite 7.3.2 → 7.3.3
  • ✅ Verified node -e \"import('@tanstack/react-start')\" exports createMiddleware
  • ✅ Verified dist/esm/index.js contains the export * re-export at runtime
  • ❌ Importing directly from @tanstack/start-client-core bypasses the issue but breaks the client bundle (leaks node:async_hooks into browser code — the react-start facade is intentionally protecting against this via export conditions)

Related

  • #7285 — same root cause, HMR re-evaluation surface (closed as "fix in Vite 8" but reporters confirm bug persists on Vite 8)
  • #5673 — original report against Nitro
  • Vite PR linked in #7285: https://github.com/vitejs/vite/commit/235140b2d

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with dist/esm/index.js and the Vite module-runner.js stack trace, then reproduce the first SSR request using the middleware in src/middlewares/auth.ts. Compare behavior with and without the ssr.optimizeDeps.include entries in vite.config.ts. Done means cold-start SSR resolves createMiddleware without the workaround while preserving the client bundle, with HMR behavior checked against #7285.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript, vite
Domain
backend, build-system, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.