remix-run/react-router · critical · Error

Invalid payload type

Error message

Invalid payload type

What it means

`createRouterFromPayload` bootstraps the client-side DataRouter from the initial RSC stream and only knows how to build a router from a payload of `type: "render"`. If the payload handed to it is any other type (action, redirect, manifest), it throws immediately. The render payload is normally produced by the framework's server entry; seeing this error means the initial payload was generated or routed incorrectly.

Source

Thrown at packages/react-router/lib/rsc/browser.tsx:276

  payload,
}: {
  payload: RSCPayload;
  createFromReadableStream: BrowserCreateFromReadableStreamFunction;
  fetchImplementation: (request: Request) => Promise<Response>;
  getContext: RouterInit["getContext"] | undefined;
}): {
  router: DataRouter;
  routeModules: RouteModules;
} {
  const globalVar = window as WindowWithRouterGlobals;

  if (globalVar.__reactRouterDataRouter && globalVar.__reactRouterRouteModules)
    return {
      router: globalVar.__reactRouterDataRouter,
      routeModules: globalVar.__reactRouterRouteModules,
    };

  if (payload.type !== "render") throw new Error("Invalid payload type");

  let { clientVersion } = payload;

  globalVar.__reactRouterRouteModules =
    globalVar.__reactRouterRouteModules ?? {};
  populateRSCRouteModules(globalVar.__reactRouterRouteModules, payload.matches);

  let routes = payload.matches.reduceRight((previous, match) => {
    const route: DataRouteObject = createRouteFromServerManifest(
      match,
      payload,
    );
    if (previous.length > 0) {
      route.children = previous;
    } else if (!route.index) {
      route.children = [];
    }
    return [route];

View on GitHub (pinned to 7aea711dd1)

Solutions

  1. Ensure the initial hydration request is served the framework's render payload (same URL, `React-Router-Resource`/RSC negotiation intact).
  2. If you cache RSC payloads at the edge, key them by request type so render, manifest, and action payloads are never interchanged.
  3. In custom entries, pass the payload received for the document request directly to `RSCHydratedRouter`, not one from an action/manifest fetch.
  4. Regenerate any recorded payload fixtures with the current version.
Defensive patterns

Strategy: type-guard

Validate before calling

if (payload.type !== "render") {
  throw new Error("Cannot hydrate: expected render payload");
}
const { router } = createRouterFromPayload({ payload, ... });

Type guard

type RenderPayload = Extract<RSCPayload, { type: "render" }>;
const isRenderPayload = (p: RSCPayload): p is RenderPayload => p.type === "render";

Prevention

When it happens

Trigger: Passing a non-render payload (e.g., a manifest or action response captured from another request) into the hydration path via a custom `RSCHydratedRouter`/`createRouterFromPayload` usage; a custom server answering the initial document request with a redirect/action payload instead of the render stream; test harnesses reusing a stale payload fixture.

Common situations: Writing custom RSC server entries or playgrounds that fetch the wrong endpoint for hydration; integration tests hydrating from a recorded action response; edge workers that cache an RSC payload of the wrong type for the document URL.

Related errors


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