remix-run/react-router · warning

Detected manifest version mismatch, reloading...

Error message

Detected manifest version mismatch, reloading...

What it means

With lazy route discovery (fog of war), the browser fetches route manifest patches from /__manifest. If a patch response carries a version that differs from the manifest baked into the running page (typical after a new deploy while a tab stayed open), handleClientVersionMismatch stores the new version in sessionStorage (a loop guard — one reload attempt per version) and hard-navigates window.location to errorReloadPath. The never-resolving promise then stalls the initiator so the browser reloads instead of flashing an ErrorBoundary. This is an automatic recovery mechanism, not a crash.

Source

Thrown at packages/react-router/lib/dom/ssr/fog-of-war.ts:285

  try {
    // This will hard reload the destination path on navigations, or the
    // current path on fetcher calls
    if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === version) {
      // We've already tried fixing for this version, don't try again to
      // avoid loops - just let this navigation/fetch 404
      console.error(
        "Unable to discover routes due to manifest version mismatch.",
      );
      return true;
    }

    sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, version);
  } catch {
    // Session storage unavailable
  }

  window.location.href = errorReloadPath;
  console.warn("Detected manifest version mismatch, reloading...");

  // Stall here and let the browser reload and avoid triggering a flash of
  // an ErrorBoundary if we threw (same thing we do in `loadRouteModule()`)
  await new Promise(() => {
    // check out of this hook cause the DJs never gonna re[s]olve this
  });
  return true;
}

export async function fetchAndApplyManifestPatches(
  paths: string[],
  errorReloadPath: string | null,
  manifest: AssetsManifest,
  routeModules: RouteModules,
  ssr: boolean,
  isSpaMode: boolean,
  basename: string | undefined,
  manifestPath: string,

View on GitHub (pinned to 7aea711dd1)

Solutions

  1. Treat it as self-healing — no app code change is required; the reload syncs the tab to the new build
  2. Serve the manifest endpoint (and index HTML) with `Cache-Control: no-store` so patch fetches always hit the current origin build
  3. Deploy atomically (build to a new directory, swap a symlink) so HTML, assets, and manifest flip versions together
  4. If it reload-loops, the loop guard logs 'Unable to discover routes due to manifest version mismatch.' — verify all instances serve the same build and purge the CDN cache for /__manifest and HTML
Defensive patterns

Strategy: retry

Try / catch

// framework-internal recovery: one guarded hard reload per manifest version
// (sessionStorage key 'react-router-manifest-version' prevents loops).
// App-level equivalent if you surface your own version banner:
try {
  let res = await fetch("/__manifest?version=" + window.__remixManifest.version);
  if (!res.ok) location.reload();
} catch { /* offline: retry on next focus */ }

Prevention

When it happens

Trigger: A navigation or fetcher call to a not-yet-discovered route returns a version-mismatched manifest patch while an errorReloadPath is available — i.e., the server is serving a newer build than the one the current document was rendered from.

Common situations: Users with long-lived tabs across a deploy; CDN or edge caching of /__manifest (or the HTML) serving stale versions; rolling deploys where different instances serve different builds; multiple app versions behind one domain.

Related errors


AI-assisted analysis of remix-run/react-router@7aea711dd1 (2026-08-18). Data as JSON: /api/errors/e5b3263d4c6c8215. Report an issue: GitHub.