Which project does this relate to?
Start
Describe the bug
When running TanStack Start in a pnpm monorepo (Rush in this case), the client fails to hydrate with a 404 error for the virtual client entry module:
GET http://localhost:3000/@id/virtual:tanstack-start-client-entry net::ERR_ABORTED 404 (Not Found)
Uncaught (in promise) TypeError: Failed to fetch dynamically imported module: http://localhost:3000/@id/virtual:tanstack-start-client-entry
The SSR renders correctly, but client-side hydration completely fails.
Root Cause Analysis
The tanstackStart() plugin calculates the default client entry path relative to its own location:
const defaultEntryDir = path.resolve(currentDir, "..", "..", "plugin", "default-entry")
In a pnpm monorepo, the package is symlinked to the pnpm store:
node_modules/@tanstack/react-start -> ../../../../common/temp/node_modules/.pnpm/@tanstack+react-start@1.135.2_.../
The plugin sets up a Vite alias from virtual:tanstack-start-client-entry to this symlinked path. However, when the browser requests /@id/virtual:tanstack-start-client-entry, Vite's module server doesn't intercept the request - it falls through to TanStack Start's catch-all router middleware, which returns a 404 HTML page.
Workaround
Creating a local src/client.tsx file bypasses the default entry resolution:
import { StrictMode, startTransition } from 'react'
import { hydrateRoot } from 'react-dom/client'
import { StartClient } from '@tanstack/react-start/client'
startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<StartClient />
</StrictMode>,
)
})
This works because the plugin then uses the local file instead of resolving to the pnpm store path.
Your Example Website or App
This occurs in a Rush monorepo with pnpm. A similar issue was reported by an NX monorepo user: https://www.answeroverflow.com/m/1446212939764990075
Steps to Reproduce the Bug or Issue
- Create a TanStack Start app inside a pnpm monorepo (Rush, Turborepo, or plain pnpm workspaces)
- Run
vite dev
- Open browser - SSR works but console shows the 404 error
- Any client-side interactivity fails
Expected behavior
The virtual client entry module should be served correctly by Vite regardless of whether the package is symlinked.
Screenshots or Videos
No response
Platform
- Start Version: 1.135.2
- Vite Version: 7.2.2
- OS: macOS
- Package Manager: pnpm (Rush monorepo)
Additional context
The official start-workos example does not include a src/client.tsx, confirming this file should not be required. The issue appears to be in how Vite 7's Environment API handles aliases pointing to symlinked paths in the pnpm store.
Which project does this relate to?
Start
Describe the bug
When running TanStack Start in a pnpm monorepo (Rush in this case), the client fails to hydrate with a 404 error for the virtual client entry module:
The SSR renders correctly, but client-side hydration completely fails.
Root Cause Analysis
The
tanstackStart()plugin calculates the default client entry path relative to its own location:In a pnpm monorepo, the package is symlinked to the pnpm store:
The plugin sets up a Vite alias from
virtual:tanstack-start-client-entryto this symlinked path. However, when the browser requests/@id/virtual:tanstack-start-client-entry, Vite's module server doesn't intercept the request - it falls through to TanStack Start's catch-all router middleware, which returns a 404 HTML page.Workaround
Creating a local
src/client.tsxfile bypasses the default entry resolution:This works because the plugin then uses the local file instead of resolving to the pnpm store path.
Your Example Website or App
This occurs in a Rush monorepo with pnpm. A similar issue was reported by an NX monorepo user: https://www.answeroverflow.com/m/1446212939764990075
Steps to Reproduce the Bug or Issue
vite devExpected behavior
The virtual client entry module should be served correctly by Vite regardless of whether the package is symlinked.
Screenshots or Videos
No response
Platform
Additional context
The official
start-workosexample does not include asrc/client.tsx, confirming this file should not be required. The issue appears to be in how Vite 7's Environment API handles aliases pointing to symlinked paths in the pnpm store.