remix-run/react-router · error

${res.status} ${res.statusText}

Error message

${res.status} ${res.statusText}

What it means

With fog-of-war route discovery, the client fetches manifest patches for not-yet-discovered routes (a GET to the manifest path with ?paths=...&version=...). If the server answers with a non-2xx status, fetchAndApplyManifestPatches throws the raw `${status} ${statusText}` error (lib/dom/ssr/fog-of-war.ts:342). The status text is the real clue: it tells you why the discovery request failed server-side.

Solutions

  1. Reproduce the failing request in DevTools (look for the request with ?paths=...&version=... to the manifest path) and check the server response body/logs
  2. Ensure your host routes ALL requests through the React Router handler (catch-all rewrite/SPA fallback), including query-string URLs
  3. After deploys, verify version-mismatch handling can fire (204 + X-Remix-Reload-Document) so stale clients hard-reload
  4. Check that basename and manifest path configuration match the deployment URL space
  5. If an auth layer guards the manifest path, exempt it or return a valid patch payload

Example fix

// before: static server only serves files, unknown paths 404
// after: fall through to the app handler (express example)
app.use(express.static('build/client'))
app.get('*', createRequestHandler({ build }))
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-deploy check: the manifest patch endpoint must answer 200 with query params
const res = await fetch(`${location.origin}${manifestPath}?paths=/&version=test`);
if (!res.ok) console.error('fog-of-war discovery will fail:', res.status, res.statusText);

Try / catch

try {
  await discoverRoutes(true);
} catch (e) {
  // Non-blocking: log and let navigation-time discovery retry
  console.warn('route discovery failed', e);
}

Prevention

When it happens

Trigger: Clicking or preloading (<Link preload>, discoverRoutes(true)) a route not yet in the client manifest while the patch request returns 404/500/401/502; a new deploy invalidated the old client's URLs; a proxy strips query params or redirects the manifest request; static hosting that only serves real files returns 404 for the patched path.

Common situations: Deploying a new build while users hold old tabs (manifest version mismatch without the reload header); SPA/static hosting without a catch-all rewrite to the app; reverse proxies that block or redirect requests with long query strings; auth middleware that intercepts the manifest request and returns 401/302.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

    getManifestPath(manifestPath, basename),
    window.location.origin,
  );
  url.search = searchParams.toString();

  // If the URL is nearing the ~8k limit on GET requests, skip this optimization
  // step and just let discovery happen on link click.  We also wipe out the
  // nextPaths Set here so we can start filling it with fresh links
  if (url.toString().length > URL_LIMIT) {
    nextPaths.clear();
    return;
  }

  let serverPatches: AssetsManifest["routes"];
  try {
    let res = await fetch(url, { signal });

    if (!res.ok) {
      throw new Error(`${res.status} ${res.statusText}`);
    }

    if (
      await handleClientVersionMismatch(
        res.status === 204 && res.headers.has("X-Remix-Reload-Document"),
        manifest.version,
        errorReloadPath,
      )
    ) {
      return;
    }

    serverPatches = (await res.json()) as AssetsManifest["routes"];
  } catch (e) {
    if (signal?.aborted) return;
    throw e;
  }

View on GitHub (pinned to 7aea711dd1)