Skip to content

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

@devnomic

Description

@devnomic

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions