TanStack / TanStack/router

router-core@1.171.7+: SSR query stream never closes with ssr-query → "Serialization timeout after app render finished"

Open
#7,529 11 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

information needed
Dominant language
TypeScript
Stars
15.1k
Forks
1.9k
Avg merge
1d 20h
Merged PRs (30d)
143

Description

Bug

router-core@1.171.7+ regresses SSR streaming when used with @tanstack/react-router-ssr-query: the dehydration query stream is never closed, so the response stream hangs until the 60s serialization timeout fires ("Serialization timeout after app render finished"). Real browsers paint early; curl/bots see the full ~30–60s hang.

Root cause

PR #7497 ("fix streaming", landed in router-core@1.171.7) added a reserveStreamFastPath() fast path and guarded onRenderFinished:

// router-core 1.171.6 (works):
onRenderFinished: (listener) => renderFinishedListeners.push(listener),

// router-core 1.171.7+ (regressed):
reserveStreamFastPath() {
  if (!cleanupStarted && _serializationFinished && !streamFastPathReserved
      && renderFinishedListeners.length === 0 && !injectedHtmlBuffer && !scriptBuffer.hasPending()) {
    streamFastPathReserved = true;
    return true;
  }
  return false;
},
onRenderFinished: (listener) => {
  if (cleanupStarted || streamFastPathReserved) return;   // ← silently drops the listener
  renderFinishedListeners.push(listener);
},

@tanstack/router-ssr-query-core@1.169.1 registers the query-stream close via onRenderFinished:

// router-ssr-query-core 1.169.1, setupCoreRouterSsrQueryIntegration
router.serverSsr.onRenderFinished(() => {
  if (!queryStream.isClosed()) queryStream.close();
  ...
});

When reserveStreamFastPath() has already set streamFastPathReserved = true by the time the integration registers its onRenderFinished listener, that listener is silently dropped. queryStream is therefore never closed → serializationFinished never becomes true → transformStreamWithRouter hits the serialization timeout.

Versions

  • Broken: @tanstack/react-router@1.170.x (→ router-core@1.171.71.171.9) + @tanstack/react-router-ssr-query@1.167.1 (→ router-ssr-query-core@1.169.1)
  • Works: @tanstack/react-router@1.169.0 (→ router-core@1.169.0), or router-core@1.171.6 (pre-#7497, no guard)

Confirmed onRenderFinished is unguarded in 1.171.6 and guarded in 1.171.7, 1.171.8, 1.171.9.

Reproduction

React SSR with streaming (renderToReadableStream + transformReadableStreamWithRouter) and setupRouterSsrQueryIntegration, where a route loader uses ensureQueryData/prefetchQuery so there are dehydrated queries. On a route that resolves all queries quickly, reserveStreamFastPath() fires before the ssr-query integration registers its close listener → the stream hangs.

Expected

The latest published @tanstack/react-router-ssr-query is 1.167.1, which does not appear to account for reserveStreamFastPath, so there is currently no version combination on router-core@1.171.7+ that closes the query stream. Either:

  1. onRenderFinished should still register (or immediately invoke) the listener when the fast path is already reserved, or
  2. ship a router-ssr-query-core release coordinated with the reserveStreamFastPath change.

Happy to provide a minimal repro repo if useful.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing reserveStreamFastPath and onRenderFinished in router-core, then inspect setupRouterSsrQueryIntegration and its queryStream close listener in router-ssr-query-core. Reproduce with renderToReadableStream, transformReadableStreamWithRouter, and a loader using ensureQueryData or prefetchQuery; done means the dehydrated query stream closes and the response does not hit the serialization timeout.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
backend, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.