Skip to content

SSR produces invalid HTML (Null Byte U+0000) #7581

@niemann-felix

Description

@niemann-felix

Which project does this relate to?

Router

Describe the bug

When TanStack Start/Router performs Server-Side Rendering, it produces multiple Null Bytes (U+0000) inside the "$tsr-stream-barrier" script tag of the server-generated HTML.
According to the HTML Living Standard specification, this character is forbidden from appearing in input streams. Specifically, section "13.2.2 Parse errors" lists the "control-character-in-input-stream" error (https://html.spec.whatwg.org/multipage/parsing.html#parse-errors).

It appears that browsers and other clients simply ignore this; however, when using an HTML validator for frontend projects (like we do at our company), this issue appears consistently. When validating ssr-produced HTML with https://validator.w3.org the error Saw U+0000 in stream comes up.

Complete minimal reproducer

https://github.com/TanStack/router/tree/main/examples/react/start-basic

Steps to Reproduce the Bug

  1. Create any TanStack Start project that uses SSR. This is easily reproducible with the start-basic example from this repo.
  2. Clone this example and install dependencies: https://github.com/TanStack/router/tree/main/examples/react/start-basic
  3. Build the project: npm run build
  4. Start the server: node .output/server/index.mjs
  5. Get the SSR-produced HTML: curl http://localhost:3000/ > index.html
  6. Visit the W3 markup validation service and upload the file. (https://validator.w3.org/#validate_by_upload)
  7. Observe the error: Saw U+0000 in stream.

This issue is NOT exclusive to the start-basic example. From my testing, it occurs every time Router/Start performs server-side rendering and includes the "$tsr-stream-barrier" script tag.

Expected behavior

I expect TanStack's SSR feature to abide by HTML standards and not produce HTML validation errors by default when validating SSR-produced HTML on services like https://validator.w3.org/.

Screenshots or Videos

Null Bytes occur in these 5 places. First occurence is after __root__.

Image

Platform

  • Router / Start Version: react-router: 1.170.15, react-start: 1.168.24
  • OS: Debian Linux
  • Browser: Firefox (not relevant to this issue)
  • Browser Version: 150.0.1
  • Bundler: vite
  • Bundler Version: vite 8.0.14

(See the start-basic example)

Additional context

The null bytes seem to appear at five very specific locations in the script tag (as shown in the screenshot). From my testing, this behaviour (of exactly 5) is very consistent, no matter the project or served content.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions