feat!: remove typesafe returned errors and the inferable flag - #1987
Conversation
Handlers can no longer signal an error by returning an ORPCError; a returned ORPCError is plain output. The TReturnedError generic, the opaqueReturnedErrors flag and the handler output check are removed. With returned errors gone, inferable always equalled defined, so the field is dropped from ORPCError, its JSON body and the OpenAPI error schemas. isInferableError is renamed back to isDefinedError and the safe result exposes definedError. Implements middleapi#1982
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will improve performance by 13.23%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | plain |
163.8 µs | 140.8 µs | +16.35% |
| ⚡ | validated |
77.5 µs | 70.3 µs | +10.2% |
Tip
Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.
Comparing dinwwwh:claude/github-discussion-1982-c9762d (859c26b) with main (e99ad16)
There was a problem hiding this comment.
ℹ️ No critical issues — the refactor is thorough and verifiably green; one stale doc reference is noted in the nitpicks below.
Reviewed changes — PR #1987 removes typesafe returned errors and the inferable flag: a returned ORPCError is now plain output (a type error under an output schema), the TReturnedError generic and opaqueReturnedErrors are gone from Procedure/ProcedureClient/CallOptions/Next server-function types, ORPCError/ORPCErrorJSON/error bodies/OpenAPI error schemas drop inferable, and isInferableError/safe(...).inferableError are renamed to isDefinedError/definedError with no alias.
- Rename to
isDefinedError+definedError—isInferableError→isDefinedError(now gates onerror.defined), and thesaferesult's third elementinferableError→definedError, thread through every package, docs page, skill, playground, and type test. - Returned errors are now output —
executeProcedureInternalno longer intercepts/clones returnedORPCErrors, and builder.handlertypes drop the| AnyORPCErrorunion; type tests pin both directions (plain output without an output schema, rejected with one). - Wire/OpenAPI format change — error JSON bodies and generated error schemas drop
inferable;isORPCErrorJsonnow rejects legacy bodies that still carry it, and the msw e2e suites confirm thedefinedflag round-trips over both RPC and OpenAPI transports. - Effect integration —
handlerGendropssucceedOnORPCError, so a failedORPCErrorthrows as-is; tests assert failed-vs-returned ORPCErrors resolve to error vs. output respectively, and the docs steer.errors+errors.X()+Effect.fail. - Docs/skills/migration guide — the "Returning an
ORPCError" section is removed, the client anchor is now#using-safe-and-isdefinederror, thefrom-v1rename table and theisDefinedError/isInferableErrormigration note are cleaned up, and all recipes/skills are updated.
I verified the branch locally: full workspace tsc -b passes (including every *.test-d.ts), all affected-package vitest suites pass (client/contract/server/effect, openapi/next/trpc/msw/tanstack-query/pinia-colada/swr/ai-sdk — 202 files / 2005 tests), the root tests/ e2e suite passes (465 tests), eslint on the touched files is clean, and the JSDoc backlink checker reports 0 errors/0 warnings. Repo-wide greps confirm no isInferableError, inferableError, inferable (beyond a prose comment in client/src/error.ts), TReturnedError, or opaqueReturnedErrors remains anywhere in runtime code, docs, or tooling.
ℹ️ Nitpicks
packages/client/src/client-safe.ts:20-21— thecreateSafeClientJSDoc example still destructuresinferrableError(the old property name, with a long-standing extrar), but thesaferesult's third element is nowdefinedError. Anyone copying this snippet lands on a property that no longer exists; thesafeJSDoc inutils.tswas updated but this spot was missed.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — one commit since the prior review, addressing the previous run's only nitpick.
- Fixed the
createSafeClientJSDoc example inpackages/client/src/client-safe.ts—inferrableErroris nowdefinedError, matching the real third element of thesaferesult (SafeResultinclient/src/utils.ts). Confirmed accurate against the type and consistent with thesafeJSDoc.
No further issues; the remaining delta is a docs-only comment change with no behavioral surface.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/bun
@orpc/client
@orpc/cloudflare
@orpc/contract
@orpc/experimental-effect
@orpc/evlog
@orpc/hibernation
@orpc/json-schema
@orpc/experimental-msw
@orpc/nest
@orpc/next
@orpc/node
@orpc/openapi
@orpc/opentelemetry
@orpc/pinia-colada
@orpc/pino
@orpc/publisher
@orpc/ratelimit
@orpc/server
@orpc/shared
@orpc/swr
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/zod
commit: |
Mnigos
left a comment
There was a problem hiding this comment.
I tested 7899e79 against a copy of my app using contract-first oRPC, Effect 4.0.0-rc.112, and React/Svelte clients.
All 130 API tests pass on both beta.33 and this preview. The API and both frontends also typecheck. The integration checks use the actual routes, Effect middleware, error mapping, and OpenAPI client; session and data services are mocked.
Auth rejection, typed business errors, 503/500 responses, and Date coercion behaved as expected. Existing client code using isDefinedError continues to work.
I also confirmed the documented mixed-version incompatibility in both directions: DUPLICATE_BOOKMARK becomes MALFORMED_ORPC_RESPONSE when only one side is upgraded. Matching versions preserve the code and data.
No regressions found in these paths.
There was a problem hiding this comment.
Important
The latest commit deletes every test that pins this PR's headline behavior — a returned ORPCError is plain output and is rejected under an output schema — while the PR description still claims those type tests exist ("Type tests now assert that a returned ORPCError is output and is rejected under an output schema"). I restored all the deleted tests against the current head and every one passes, so this is a coverage regression, not a fix for failing tests.
Reviewed changes — one commit since the prior pullfrog review (859c26b, "test: remove returned ORPCError cases"), which deletes 205 lines of tests across six files.
- Deleted the returned-
ORPCErrortype tests — the "treats returnedORPCErroras output" assertions inbuilder.test-d.tsandbuilder-variants.test-d.ts, and the "does not allow returningORPCError"@ts-expect-errorcases inbuilder-variants.test-d.ts,implementer-procedure.test-d.ts, andeffect.test-d.ts. - Deleted the returned-
ORPCErrorruntime tests —effect/handler.test.tsandserver/procedure-client.test.ts, which asserted the error resolves as plain output withdefined === falseand no reconcile/clone.
⚠️ All tests pinning the PR's headline behavior were deleted
This commit removes the only coverage that verifies the core breaking behavior this PR ships: that a returned ORPCError is plain output (widening the output schema to Schema<'out' | ORPCError<'BAD_REQUEST', string>>) and that returning one under an output schema is a compile-time error. I restored every deleted test on the current head — server and effect tsc -b pass with the type tests restored, and vitest passes with the runtime tests restored — so none of them were failing; the coverage was dropped while the PR body still claims it exists. Either the tests should be restored, or the Testing section of the PR description should be corrected to match reality.
Technical details
# Deleted returned-ORPCError test coverage
## Affected sites
- packages/server/src/builder.test-d.ts — removed `treats returned ORPCError as output`
- packages/server/src/builder-variants.test-d.ts — removed 2× `treats returned ORPCError as output` + 2× `does not allow returning ORPCError`
- packages/server/src/implementer-procedure.test-d.ts — removed `does not allow returning ORPCError`
- packages/effect/src/extensions/effect.test-d.ts — removed 3× `treats returned ORPCError as output...` + 2× `does not allow returning ORPCError`
- packages/effect/src/handler.test.ts — removed `treats returned ORPCError as a regular output`
- packages/server/src/procedure-client.test.ts — removed `treats returned ORPCError as a regular output`
## Required outcome
- Restore the deleted tests (all pass against the current implementation), or
- If the removal is deliberate, update the PR body's Testing section, which currently claims these type tests exist.
## Verification performed
- Restored all six removed test groups on head `859c26b`; `pnpm --filter @orpc/server type:check`, `pnpm --filter @orpc/effect type:check`, and `pnpm vitest run packages/effect/src/handler.test.ts packages/server/src/procedure-client.test.ts` are all green.DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Handlers can no longer signal an error by returning an
ORPCError. What a handler returns is its output, what it throws is an error, and.errorsor theerror()factory is the only way to get a typesafe error. With returned errors gone theinferableflag always equalleddefined, so it is removed andisInferableErrorgoes back to its v1 nameisDefinedError.Implements the proposal in #1982.
Breaking changes
ORPCErroris plain output. Under an output schema it is a type error.Procedure,DecoratedProcedure,ProcedureClient,ProcedureClientOptions,CallOptionsand the Next.js server function types lose their trailingTReturnedErrorgeneric.opaqueReturnedErrorsis gone from the procedure definition.ORPCErrorandORPCErrorJSONno longer carryinferable. The error response body and generated OpenAPI error schemas drop the field, so clients on this build reject error bodies from earlier v2 betas that still send it.isInferableErroris renamed toisDefinedErrorwith no alias, andsafeexposesdefinedErrorinstead ofinferableError.ORPCErrorthat fails the effect is thrown as-is instead of becoming an inferable error.Performance
instanceofcheck or error clone on the handler output path..handlerno longer splits its return type withExclude/Extract.Docs
.errors, renamed the client error-handling anchor to#using-safe-and-isdefinederror, and updated the v1 migration guide, RPC protocol example, Next.js docs, skills and playgrounds.Testing
ORPCErroris output and is rejected under an output schema.