remix-run/react-router · error · Error

Unable to decode turbo-stream response

Error message

Unable to decode turbo-stream response

What it means

With the single-fetch data strategy, loader/action results come back from a `.data` request encoded as a turbo-stream. The client runs `decodeViaTurboStream` on the response body inside a try/catch; any decoding failure (body not turbo-stream bytes, corrupted stream) hits the catch, which deliberately throws this generic error because the body has already been consumed and cannot be re-read or echoed. This means the response you received was not a valid turbo-stream payload.

Source

Thrown at packages/react-router/lib/dom/ssr/single-fetch.tsx:664

    } else {
      let typed = decoded.value as SingleFetchResult;
      let routeId = targetRoutes?.[0];
      invariant(routeId, "No routeId found for single fetch call decoding");
      if ("redirect" in typed) {
        data = { redirect: typed };
      } else {
        data = { routes: { [routeId]: typed } };
      }
    }
    return { status: res.status, data };
  } catch {
    // Can't clone after consuming the body via turbo-stream so we can't
    // include the body here.  In an ideal world we'd look for a turbo-stream
    // content type here, or even X-Remix-Response but then folks can't
    // statically deploy their prerendered .data files to a CDN unless they can
    // tell that CDN to add special headers to those certain files - which is a
    // bit restrictive.
    throw new Error("Unable to decode turbo-stream response");
  }
}

// Note: If you change this function please change the corresponding
// encodeViaTurboStream function in server-runtime
export function decodeViaTurboStream(
  body: ReadableStream<Uint8Array>,
  global: Window | typeof globalThis,
) {
  return decode(body, {
    plugins: [
      (type: string, ...rest: unknown[]) => {
        // Decode Errors back into Error instances using the right type and with
        // the right (potentially undefined) stacktrace
        if (type === "SanitizedError") {
          let [name, message, stack] = rest as [
            string,
            string,

View on GitHub (pinned to 6beaca3952)

Solutions

  1. Open the network tab and inspect the failing `.data` request — the response body is almost certainly an HTML error/login page or empty; fix whatever serves it (auth redirect, 404 handler, gateway rule) to let the router's own response through.
  2. Exclude `.data` requests (by path suffix or `X-Remix-`/React Router data headers) from interceptors, service workers, and compression layers.
  3. If it happens right after a deploy, hard-refresh or clear the cache so client and server encodings match.
  4. For prerendered static hosting, verify the deployed `.data` files are byte-identical to the build output and served uncompressed-as-built.
Defensive patterns

Strategy: retry

Try / catch

// in a route error boundary
export function ErrorBoundary() {
  const error = useRouteError();
  if (error instanceof Error && /turbo-stream/.test(error.message)) {
    return <button onClick={() => window.location.reload()}>Session out of date — reload</button>;
  }
  throw error;
}

Prevention

When it happens

Trigger: A proxy, auth gateway, or service worker intercepting the `.data` request and returning an HTML login/404 page instead of the turbo-stream body; a CDN serving a stale or wrong-content-type prerendered `.data` file; double compression (gzip/brotli applied twice) mangling the stream; severe client/server version skew after a deploy where old client code decodes a new server encoding.

Common situations: Corporate proxies or API gateways rewriting responses for non-HTML content types; hosting static prerendered `.data` files with wrong MIME/compression settings; a middleware returning `json()` for `.data` requests; deploys where the browser holds an old HTML bundle that requests `.data` from a new server.

Understand the failure class

Related errors


AI-assisted analysis of remix-run/react-router@6beaca3952 (2026-08-18). Data as JSON: /api/errors/705d2de445648b52. Report an issue: GitHub.