Skip to content

fix(rpc): skip unknown meta types when deserializing - #2357

Merged
dinwwwh merged 1 commit into
mainfrom
claude/sleepy-bardeen-8zsqz5
Oct 29, 2026
Merged

dinwwwh merged 1 commit into
mainfrom
claude/sleepy-bardeen-8zsqz5

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Oct 29, 2026

Copy link
Copy Markdown
Member

Summary

Client and server code can't always be deployed in sync, so one side may receive a meta entry whose handler key it doesn't have (a custom handler added on one side only, or a built-in disabled with handlers: { url: undefined }). RPCJsonSerializer.deserializeJson used to throw type "X" is not supported., failing the whole payload.

Unknown types are now skipped: the value stays in its serialized JSON form, and everything else in the payload, including known values nested inside the unknown one, is still restored.

  • A skipped entry neither walks nor mutates the payload, so it can't reach into restored values or missing paths, and later entries that share its path prefix work as before.
  • Types are still looked up on a null-prototype map, so __proto__, constructor, toString, etc. never resolve to a handler; they're just skipped now.
  • Mistyped values for known types, bad paths and non-Blob parts still throw as before.

Trade-off

A mismatch is now silent. On the server, a value left in serialized form reaches input validation, which will usually reject it (e.g. a string where a Date is expected). On the client there is no such check, so the caller gets the serialized form. There's no option to restore the strict behaviour; happy to add one if wanted.

Changes

  • packages/client/src/rpc-json-serializer.ts: skip unknown types instead of throwing; note the behaviour in the handlers JSDoc.
  • Tests: new rpcJsonSerializer: unknown types block, and the prototype-chain tests now expect the value back unchanged (dropped the duplicate in rpc-serializer.test.ts).
  • apps/content/docs/rpc/serializer.mdx: "Handlers out of sync" note.

Testing

packages/client and tests/rpc suites, tsc, eslint, and docs:validate pass locally.


Generated by Claude Code

Client and server can run different versions with different serializer
handlers. Previously a meta entry with a handler key the receiver did not
know threw and failed the whole payload. Now such entries are skipped, so
the value stays in its serialized form while known values, including
those nested inside it, are still restored. Skipped entries neither walk
nor mutate the payload, and types are still looked up on a null-prototype
map, so prototype keys never resolve to a handler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VVaw7RZzFyYdaijHa8YrMn

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

  • Skip unknown meta types on deserialize — deserializeJson now continues past a meta entry whose type has no handler instead of throwing, so a client and server with different handler sets can still exchange payloads.
  • Null-prototype lookup preserved — __proto__ / constructor / toString still resolve to undefined and are now skipped rather than rejected, keeping the prototype-pollution guard intact.
  • Tests — added an unknown types block covering serialized-form retention and nested-value restoration; the security prototype-chain it.each now asserts the value is returned unchanged; removed the redundant throw test in rpc-serializer.test.ts.
  • Docs — :::info[Handlers out of sync] note in rpc/serializer.mdx and a JSDoc line on handlers.

I traced the skipped path through the chain / chainDepth prefix-reuse optimization: skipped entries neither walk nor mutate, and since the chain is only updated by processed entries, later entries that share a prefix reuse it exactly as before. The new tests cover both sibling and nested cases. The PR body already documents the one real trade-off (a mismatch is now silent, with no strict-mode escape hatch), which is a deliberate maintainer call, so nothing to flag inline.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
orpc 660f958 Commit Preview URL

Branch Preview URL
Oct 29 2026, 10:05 AM

@pkg-pr-new

pkg-pr-new Bot commented Oct 29, 2026

Copy link
Copy Markdown
More templates

@orpc/ai-sdk

npm i https://pkg.pr.new/@orpc/ai-sdk@2357

@orpc/arktype

npm i https://pkg.pr.new/@orpc/arktype@2357

@orpc/bun

npm i https://pkg.pr.new/@orpc/bun@2357

@orpc/client

npm i https://pkg.pr.new/@orpc/client@2357

@orpc/cloudflare

npm i https://pkg.pr.new/@orpc/cloudflare@2357

@orpc/contract

npm i https://pkg.pr.new/@orpc/contract@2357

@orpc/effect

npm i https://pkg.pr.new/@orpc/effect@2357

@orpc/evlog

npm i https://pkg.pr.new/@orpc/evlog@2357

@orpc/hibernation

npm i https://pkg.pr.new/@orpc/hibernation@2357

@orpc/json-schema

npm i https://pkg.pr.new/@orpc/json-schema@2357

@orpc/experimental-lock

npm i https://pkg.pr.new/@orpc/experimental-lock@2357

@orpc/mcp

npm i https://pkg.pr.new/@orpc/mcp@2357

@orpc/msw

npm i https://pkg.pr.new/@orpc/msw@2357

@orpc/nest

npm i https://pkg.pr.new/@orpc/nest@2357

@orpc/next

npm i https://pkg.pr.new/@orpc/next@2357

@orpc/node

npm i https://pkg.pr.new/@orpc/node@2357

@orpc/openapi

npm i https://pkg.pr.new/@orpc/openapi@2357

@orpc/opentelemetry

npm i https://pkg.pr.new/@orpc/opentelemetry@2357

@orpc/pinia-colada

npm i https://pkg.pr.new/@orpc/pinia-colada@2357

@orpc/pino

npm i https://pkg.pr.new/@orpc/pino@2357

@orpc/publisher

npm i https://pkg.pr.new/@orpc/publisher@2357

@orpc/ratelimit

npm i https://pkg.pr.new/@orpc/ratelimit@2357

@orpc/server

npm i https://pkg.pr.new/@orpc/server@2357

@orpc/shared

npm i https://pkg.pr.new/@orpc/shared@2357

@orpc/swr

npm i https://pkg.pr.new/@orpc/swr@2357

@orpc/tanstack-query

npm i https://pkg.pr.new/@orpc/tanstack-query@2357

@orpc/trpc

npm i https://pkg.pr.new/@orpc/trpc@2357

@orpc/valibot

npm i https://pkg.pr.new/@orpc/valibot@2357

@orpc/zod

npm i https://pkg.pr.new/@orpc/zod@2357

commit: 660f958

@codecov

codecov Bot commented Oct 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

1 warning

⚠️ 6 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

✅ 28 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing claude/sleepy-bardeen-8zsqz5 (660f958) with main (82a7108)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@dinwwwh
dinwwwh merged commit 344b3c3 into main Oct 29, 2026
14 checks passed
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.

2 participants