[Start RSC] SerovalUnsupportedTypeError on RawStream during dev SSR — production unaffected
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.1k
- Forks
- 1.9k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 143
Description
Which project does this relate to?
Start
Describe the bug
@tanstack/react-start/rscloaders that return values fromcreateCompositeComponent(...)` crash during dev-mode SSR dehydration. Production builds are unaffected — same code, same route, dev throws, prod renders.
Server log
Serialization error: SerovalUnsupportedTypeError: The value [object Object] of type "object" cannot be parsed/serialized.
value: RawStream { stream: ReadableStream, hint: 'text' }
at parseObjectPhase2 (seroval/src/core/context/sync-parser.ts:743)
at parseObject (seroval/src/core/context/sync-parser.ts:773)
at parseSOS (seroval/src/core/context/sync-parser.ts:815)
at StreamParsePluginContext.parse (seroval/src/core/context/sync-parser.ts:150)
at Object.stream (router-core/src/ssr/serializer/transformer.ts:193)
Client console
Invariant failed: Expected to find a dehydrated data on window.$\_TSR.router, but we did not. Please file an issue!
at hydrate (ssr-client.js:31)
at hydrateStart (hydrateStart.js:27)
Dehydration aborts mid-write, so window.$_TSR.router never reaches the client and hydrate() throws on load.
Root cause
Vite dev runs each environment (client, ssr, rsc, nitro) through a separate module-runner, so the RawStream class at packages/router-core/src/ssr/serializer/RawStream.ts is loaded once per env with distinct constructor identities.
packages/react-start-rsc/src/serialization.server.ts:183 constructs new RawStream(stream, { hint: 'text' }) in the RSC env. RawStreamSSRPlugin.test() at packages/router-core/src/ssr/serializer/RawStream.ts:323-325 runs in a different env and evaluates value instanceof RawStream — which returns false cross-realm. The plugin doesn't claim the value, the seroval parser falls through, parseObjectPhase2 throws SerovalUnsupportedTypeError at seroval/src/core/context/sync-parser.ts:743. The same instanceof pattern at RawStream.ts:407 in createRawStreamRPCPlugin has the same flaw.
Production Rollup deduplicates everything into a single bundle per target → one RawStream constructor → instanceof works → no crash.
I've validated locally with a bun patch @tanstack/router-core@1.169.2 that swaps instanceof RawStream for a Symbol.for('@tanstack/router.RawStream') brand check (and adds the brand in the constructor). With the patch, both /rsc-test and complex RSC routes start working in dev with zero seroval errors. Production behavior is unchanged. PR with the fix at #.
Complete minimal reproducer
https://github.com/timelessco/reform
Steps to Reproduce the Bug
Branch:
main. The routesrc/routes/rsc-test.tsxis the docscreateCompositeComponentexample pattern verbatim — see Server Components docs → Composite (slots). It reproduces directly afterbun install && bun dev.
1. `git clone https://github.com/timelessco/reform && cd reform`
2. `bun install` (or `pnpm install` / `npm install`)
3. `bun dev`
4. Open `http://localhost:3000/rsc-test`
Observed:
- Server terminal logs `SerovalUnsupportedTypeError: ... value: RawStream { stream: ReadableStream, hint: 'text' }` from `transformer.ts:193`.
- Browser console throws `Invariant failed: Expected to find a dehydrated data on window.$_TSR.router`.
- The page renders the dev-server error overlay instead of the RSC test fixture.
For comparison, the same route on the deployed Vercel build renders correctly:
https://better-forms-mu.vercel.app/rsc-test
The route source — identical to the docs example, ~50 LOC:
https://github.com/timelessco/reform/blob/main/src/routes/rsc-test.tsx
Expected behavior
I expected /rsc-test to render in dev exactly the way it renders in production: the loader return is dehydrated, the RSC payload streams, the client hydrates without error.
Instead, dev-mode SSR aborts dehydration mid-write because Vite's per-environment module-runner causes value instanceof RawStream in RawStreamSSRPlugin.test to return false, so the plugin doesn't claim the value and seroval throws.
Screenshots or Videos
No response
Platform
- Router / Start Version: @tanstack/react-start 1.167.64, @tanstack/react-router 1.169.2, @tanstack/router-core 1.169.2, @tanstack/router-plugin 1.167.34
- OS: macOS 25.3.0 (Darwin)
- Browser: Chrome
- Browser Version: latest
- Bundler: vite (with @vitejs/plugin-rsc 0.5.24, @vitejs/plugin-react 5.2.0)
- Bundler Version: 7.3.1
Additional context
I have patch the package that you give addtional context and for me that actuall resolved the issues, here is the pr link for reference : https://github.com/timelessco/reform/pull/73
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with packages/router-core/src/ssr/serializer/RawStream.ts, especially RawStreamSSRPlugin.test and createRawStreamRPCPlugin, then inspect packages/react-start-rsc/src/serialization.server.ts:183 and router-core/src/ssr/serializer/transformer.ts:193. Run the reform reproducer with bun dev and open /rsc-test. Done means dev SSR dehydrates the loader result, streams the RSC payload, and hydrates without the serialization or missing dehydrated-data errors.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript, vite
- Domain
- backend, full-stack
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100