Which project does this relate to?
Router
Describe the bug
I am encountering a fatal Uncaught Error: Invariant failed crash during SSR hydration when my environment resolves the CommonJS (CJS) version of @tanstack/router-core (e.g., in certain Vite/SSR, Cloudflare Wrangler, or Node.js environments).
The crash occurs within the hydrate function in ssr-client.cjs. An aggressive invariant check assumes a child match must exist when isSpaMode is true. This is particularly problematic during hydration on 404/Error pages or when minor route ID mismatches occur, as it results in a "white screen of death" rather than a graceful recovery.
The Inconsistency
There is a direct logic mismatch between the ESM and CJS builds in @tanstack/router-core (v1.163.3):
1. The Crashing Code (CJS)
File: node_modules/@tanstack/router-core/dist/cjs/ssr/ssr-client.cjs
// Line ~189
if (isSpaMode) {
const match = matches;
// This throws a fatal error if match is undefined
invariant(
match,
"Expected to find a match below the root match in SPA mode."
);
setMatchForcePending(match);
// ...
}
2. The Safe Code (ESM)
File: node_modules/@tanstack/router-core/dist/esm/ssr/ssr-client.js
if (isSpaMode) {
const match = matches;
// The ESM version handles the missing match gracefully
if (match) {
setMatchForcePending(match);
// ...
} else {
console.warn("SPA hydration: no match available for pending state");
}
}
Impact
This discrepancy means the exact same application code may work perfectly in some environments (ESM) but crash in others (CJS) due to an inconsistent build artifact. In SSR contexts like Cloudflare Pages/Wrangler, the CJS entry point is often prioritised, leading to unavoidable crashes during hydration on non-existent routes.
Your Example Website or App
https://github.com/TanStack/router
Steps to Reproduce the Bug or Issue
Reproduction Steps
- Initialise a TanStack Start / Router project with SSR/Prerendering enabled.
- Force the environment to resolve CommonJS for @tanstack/router-core. This commonly occurs in production environments like Cloudflare Pages (Wrangler) or specific Node.js SSR setups where CJS is prioritized over ESM for core utilities.
- Trigger a hydration mismatch on a 404 route. For example: Navigate directly to a non-existent URL (e.g., /some-404-page). The server prerenders the page, but during hydration, the router calculates isSpaMode as true (often due to a minor ID mismatch or because it's a 404 transition).
- Observe the Hydration Logic: The hydrate function in node_modules/@tanstack/router-core/dist/cjs/ssr/ssr-client.cjs executes.
- The Crash: Because it is a 404/Error route, matches.length is 1 (Root only), meaning matches[1] is undefined.
- Fatal Error: The console will show: Uncaught Error: Invariant failed: Expected to find a match below the root match in SPA mode. This halts the JavaScript execution and prevents the application from becoming interactive.
Expected behavior
The CommonJS (CJS) build should be functionally identical to the ESM build. Specifically, the hydrate function should handle missing hydration data or child matches gracefully (via console.warn) rather than throwing fatal invariant errors that crash the entire application.
Proposed Fix for dist/cjs/ssr/ssr-client.cjs
The invariant should be replaced with a null-check, matching the existing logic found in the ESM version:
// Expected logic in dist/cjs/ssr/ssr-client.cjs
if (isSpaMode) {
const match = matches;[1]
// 1. Remove the fatal invariant:
// invariant(match, "Expected to find a match below the root match in SPA mode.");
// 2. Implement the safe check used in the ESM version:
if (match) {
setMatchForcePending(match);
match._displayPending = true;
match._nonReactive.displayPendingPromise = loadPromise;
loadPromise.then(() => {
batch.batch(() => {
// ... (remaining pending logic)
});
});
} else {
// Graceful fallback for 404/Error routes during hydration
console.warn("SPA hydration: no match available for pending state");
}
}
By aligning these two builds, the router becomes resilient to minor hydration mismatches in CJS environments, preventing the "white screen of death" and allowing the application to remain interactive.
Screenshots or Videos
No response
Platform
- Router / Start Version: 1.163.3
- OS: Windows (win32)
- Browser: Any modern browser (Chrome/Safari/Firefox/Edge)
- Bundler: Vite
- Bundler Version: 7.3.1
- Deployment Platform: Cloudflare Pages / Wrangler (Relevant for CJS resolution)
Additional context
I've put an empty link into the reproduction URL field because this is a build-artifact inconsistency bug between the CJS and ESM distributions that is difficult to replicate in a browser-based StackBlitz (which defaults to ESM). I have provided direct UNPKG links to the conflicting files in the @tanstack/router-core package below:
Which project does this relate to?
Router
Describe the bug
I am encountering a fatal
Uncaught Error: Invariant failedcrash during SSR hydration when my environment resolves the CommonJS (CJS) version of@tanstack/router-core(e.g., in certain Vite/SSR, Cloudflare Wrangler, or Node.js environments).The crash occurs within the
hydratefunction inssr-client.cjs. An aggressive invariant check assumes a child match must exist whenisSpaModeistrue. This is particularly problematic during hydration on 404/Error pages or when minor route ID mismatches occur, as it results in a "white screen of death" rather than a graceful recovery.The Inconsistency
There is a direct logic mismatch between the ESM and CJS builds in
@tanstack/router-core(v1.163.3):1. The Crashing Code (CJS)
File:
node_modules/@tanstack/router-core/dist/cjs/ssr/ssr-client.cjs2. The Safe Code (ESM)
File: node_modules/@tanstack/router-core/dist/esm/ssr/ssr-client.js
Impact
This discrepancy means the exact same application code may work perfectly in some environments (ESM) but crash in others (CJS) due to an inconsistent build artifact. In SSR contexts like Cloudflare Pages/Wrangler, the CJS entry point is often prioritised, leading to unavoidable crashes during hydration on non-existent routes.
Your Example Website or App
https://github.com/TanStack/router
Steps to Reproduce the Bug or Issue
Reproduction Steps
Expected behavior
The CommonJS (CJS) build should be functionally identical to the ESM build. Specifically, the
hydratefunction should handle missing hydration data or child matches gracefully (viaconsole.warn) rather than throwing fatal invariant errors that crash the entire application.Proposed Fix for
dist/cjs/ssr/ssr-client.cjsThe invariant should be replaced with a null-check, matching the existing logic found in the ESM version:
By aligning these two builds, the router becomes resilient to minor hydration mismatches in CJS environments, preventing the "white screen of death" and allowing the application to remain interactive.
Screenshots or Videos
No response
Platform
Additional context
I've put an empty link into the reproduction URL field because this is a build-artifact inconsistency bug between the CJS and ESM distributions that is difficult to replicate in a browser-based StackBlitz (which defaults to ESM). I have provided direct UNPKG links to the conflicting files in the @tanstack/router-core package below: