Skip to content

renderRouterToStream returns a 200 for an aborted bot (isbot) SSR render #7929

Description

@roduyemi

Which project does this relate to?

Start

Describe the bug

A recent change to renderRouterToStream (see below), means when request.signal aborts, we now get a status of 200 instead of an error. This change was introduced in v1.170.9 (PR #7497).

// 1.170.8 (before)
if (isbot(request.headers.get('User-Agent'))) {
  await stream.allReady
}

// 1.170.9 → 1.170.18 (after)
if (isbot(request.headers.get('User-Agent'))) {
  await waitForReadyOrAbort(stream.allReady, request.signal)
}

Complete minimal reproducer

https://github.com/roduyemi/tanstack-start-ssr-bot-abort-200

Steps to Reproduce the Bug

  1. Clone the repo and run
pnpm install && pnpm dev.
  1. Open http://localhost:3000/repro.
    /repro does a server side render of /slow (a route that suspends) with a Googlebot user-agent, then aborts the request about 100ms in (while it's still waiting on the bot's allReady).
  2. See a render that was aborted before it finished has a status of 200.
{ "status": 200, "hasTitle": true, "hasSlowRouteContent": false }

Expected behavior

An aborted bot render should surface as an error (e.g. 5xx) - or a status other than 200 (success). This was the behaviour before 1.170.10, when await stream.allReady rejected on abort.

Screenshots or Videos

No response

Platform

  • Router / Start Version: @tanstack/react-router 1.170.18, @tanstack/react-start 1.168.33
  • OS: macOS
  • Browser: N/A — server-side SSR bug, reproduced via curl with a Googlebot UA (any browser)
  • Browser Version: N/A
  • Bundler: Vite 8
  • Bundler Version: 8.2.0

Additional context

It's worse on workerd in production. When the abort races the client read, stream.cancel() often discards the buffered shell before it's flushed, so we end up serving the crawler a completely empty, 0 byte 200, which then gets indexed (not great for SEO 😢 ).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions