Skip to content

fix: fallback to structured-clone for RPC error envelopes#8

Merged
antfu merged 4 commits into
devframes:mainfrom
baseballyama:fix/vite-devtools-335
May 13, 2026
Merged

fix: fallback to structured-clone for RPC error envelopes#8
antfu merged 4 commits into
devframes:mainfrom
baseballyama:fix/vite-devtools-335

Conversation

@baseballyama
Copy link
Copy Markdown
Contributor

This PR came from vitejs/devtools#336.


Description

jsonSerializable: true declares the result shape of an RPC function, but the WS transport applied strictJsonStringify to every outgoing message routed to such a method — including the error envelope ({ t: 's', i, e }) emitted when the handler throws. Because Error instances aren't JSON-serializable, this crashed the serializer with DF0020 and masked the original DTK0011 entirely, taking the dev server down with it (reproducible on StackBlitz / WebContainer).

Skip the strict-JSON path for error envelopes on both ws-server and ws-client transports so thrown values round-trip via structured-clone, and the underlying failure surfaces on the caller instead.

Linked Issues

close: vitejs/devtools#335

Additional context

N/A

Copilot AI review requested due to automatic review settings May 12, 2026 12:47
@netlify
Copy link
Copy Markdown

netlify Bot commented May 12, 2026

Deploy Preview for devfra ready!

Name Link
🔨 Latest commit e46ae58
🔍 Latest deploy log https://app.netlify.com/projects/devfra/deploys/6a03d800bb7a1a00081069be
😎 Deploy Preview https://deploy-preview-8--devfra.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a WS RPC transport regression where jsonSerializable: true methods could crash the strict JSON serializer when the handler throws (because the error envelope contains a thrown value like an Error). It routes error envelopes through the structured-clone wire format instead, allowing the original failure to propagate to the caller.

Changes:

  • Update WS server transport serialization to bypass strict JSON for error envelopes ({ t: 's', i, e }) and use structured-clone instead.
  • Update WS client transport serialization with the same error-envelope structured-clone fallback behavior.
  • Add a regression test ensuring a thrown error results in a rejection rather than a DF0020 serialization crash.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
packages/devframe/src/rpc/transports/ws-server.ts Avoids strict JSON encoding for error responses so thrown values don’t crash serialization.
packages/devframe/src/rpc/transports/ws-client.ts Mirrors server behavior to ensure error envelopes use structured-clone instead of strict JSON.
packages/devframe/src/rpc/transports/ws.test.ts Adds regression coverage for thrown errors in jsonSerializable: true RPC methods over WS.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +75 to +79
const { wss } = attachWsRpcTransport(server, { port: PORT, host: HOST, definitions: definitions as any })

try {
const client = createRpcClient<typeof serverFunctions, Record<string, never>>({}, {
channel: createWsRpcChannel({ url: WS_URL, definitions: definitions as any }),
Comment on lines +60 to +63
const PORT = 3334
const HOST = '127.0.0.1'
const WS_URL = `ws://${HOST}:${PORT}`

antfu added 3 commits May 13, 2026 10:04
- Drop unnecessary `as any` casts on `definitions` — the local map type
  is structurally compatible with the transport's expected shape.
- Allocate the port via `get-port-please` instead of hardcoding `3334`,
  matching the pattern in `adapters/__tests__/dev.test.ts` and avoiding
  flakes when the port is already in use.
Extends the structured-clone error fallback shipped for the WS RPC
transport to the dump and MCP surfaces:

- Dumps capture `Error.cause` chains and own properties instead of
  reducing to `{ message, name }`. Static-dump promotes error-bearing
  records to structured-clone per-record (via new
  `recordSerializations`/`fallbackSerialization` manifest fields) so
  `jsonSerializable: true` functions still round-trip thrown errors
  losslessly.
- MCP replaces the bare `JSON.stringify` with a coercing helper for
  `BigInt`/`Date`/`Map`/`Set`/`Error`/cycles, since MCP wire format is
  text-only and cannot use the `s:` structured-clone prefix. Error
  responses now include `name` and `cause.message`.
`serializeDumpError` already flattens the common rich-error shape
(`message`, `name`, recursive `cause`) into a JSON-safe object, so
`jsonSerializable: true` functions that throw ordinary errors round-trip
through strict JSON without any per-record promotion.

The edge case — an error with non-JSON own properties (e.g. a `Map`
attached to the thrown error) — now surfaces as `DF0020` at build time,
consistent with the rest of the `jsonSerializable: true` contract.
@antfu antfu merged commit e963e8f into devframes:main May 13, 2026
11 checks passed
@baseballyama baseballyama deleted the fix/vite-devtools-335 branch May 13, 2026 01:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: vite:anonymous:auth fails on StackBlitz: DF0020 masks DTK0011 (Error in jsonSerializable: true response)

3 participants