Skip to content

fix(contract): only reserve then in the handler errors map - #2117

Merged
dinwwwh merged 1 commit into
middleapi:mainfrom
dinwwwh:claude/orpc-error-constructors-unwrap-10db1f
Sep 30, 2026
Merged

dinwwwh merged 1 commit into
middleapi:mainfrom
dinwwwh:claude/orpc-error-constructors-unwrap-10db1f

Conversation

@dinwwwh

@dinwwwh dinwwwh commented Sep 30, 2026

Copy link
Copy Markdown
Member

The handler errors map now reserves only then, instead of every key in RECURSIVE_CLIENT_UNWRAP_KEYS as #2113 did. then is the only key the runtime reads on its own (promise resolution), so it is the only one that caused a real failure. The others are only reached by explicitly converting or serializing the map. This narrows the behavior change from #2113.

Fixes

  • errors.toString(), errors.valueOf(), errors.toJSON(), errors.bind(), errors.call() and errors.apply() build errors with those codes again.
  • await errors and returning errors from an async handler still settle instead of hanging.
  • Adding a key to the client's unwrap set no longer silently takes that name away from error codes.

Testing

  • The regression test now checks only symbols and then. The Object.prototype lookup test uses toString() again and fails if toString is reserved.
  • Contract and server tests (687), eslint, and the @orpc/contract type-check pass.

The errors map is not a recursive proxy, so reusing the client's
RECURSIVE_CLIENT_UNWRAP_KEYS reserved more names than needed. Only
`then` is read implicitly (by promise resolution); the other keys are
only reached through explicit conversion or serialization of the map.
Error codes such as `toString`, `valueOf`, and `toJSON` can be built
from `errors` again.

@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 — narrowed error-map key reservation plus its regression tests.

  • Proxy guard narrowed to then — createORPCErrorConstructorMap now returns the raw value only for symbols and the literal key then, dropping the RECURSIVE_CLIENT_UNWRAP_KEYS dependency. The #2113 hang fix is preserved (the map is still non-thenable), while toString/valueOf/toJSON/bind/call/apply build error constructors again.
  • Tests realigned — the symbol/then test keeps its await assertion; the Object.prototype test switches back to toString() and asserts code === 'toString', which is exactly the assertion that fails if toString is reserved again.

The one behavior deliberately reintroduced from #2113 — String(errors) can throw and JSON.stringify(errors) can again emit a synthetic toJSON error — is a conscious tradeoff argued in the PR body and is only reachable through explicit conversion/serialization. No internal caller or test relies on it.

Verified locally: pnpm vitest run packages/contract/src/error-factory.test.ts → 24 passed.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@codecov

codecov Bot commented Sep 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@pkg-pr-new

pkg-pr-new Bot commented Sep 30, 2026

Copy link
Copy Markdown
More templates

@orpc/ai-sdk

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

@orpc/arktype

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

@orpc/bun

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

@orpc/client

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

@orpc/cloudflare

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

@orpc/contract

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

@orpc/experimental-effect

npm i https://pkg.pr.new/@orpc/experimental-effect@2117

@orpc/evlog

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

@orpc/hibernation

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

@orpc/json-schema

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

@orpc/experimental-lock

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

@orpc/experimental-msw

npm i https://pkg.pr.new/@orpc/experimental-msw@2117

@orpc/nest

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

@orpc/next

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

@orpc/node

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

@orpc/openapi

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

@orpc/opentelemetry

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

@orpc/pinia-colada

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

@orpc/pino

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

@orpc/publisher

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

@orpc/ratelimit

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

@orpc/server

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

@orpc/shared

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

@orpc/swr

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

@orpc/tanstack-query

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

@orpc/trpc

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

@orpc/valibot

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

@orpc/zod

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

commit: 7b377b8

@dinwwwh
dinwwwh merged commit c5d193c into middleapi:main Sep 30, 2026
10 checks passed
@codspeed

codspeed Bot commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 30 untouched benchmarks


Comparing dinwwwh:claude/orpc-error-constructors-unwrap-10db1f (7b377b8) with main (36b8bd9)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (63b2053) during the generation of this report, so 36b8bd9 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

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.

1 participant