Repository navigation
feat(client): introduce ResponseError for carrying response details - #2265
Conversation
Add `ResponseError`, an error carrying the resolved response a link received. `RPCLink` and `OpenAPILink` now set it as the `cause` of the `ORPCError` they decode from an error response, so its status and headers stay available on the client. `MalformedResponseError` now extends `ResponseError`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RggAhK2fPDzS1W965ZaJQY
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
orpc | 4a90670 | Commit Preview URL Branch Preview URL |
Oct 19 2026, 08:12 AM |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — Introduces a public ResponseError carrying the resolved response and attaches it as the cause of ORPCErrors decoded from error responses; MalformedResponseError is refactored to extend it; docs and tests updated.
- New
ResponseErrorclass (packages/client/src/error.ts:173) — extendsError, exposesresponse, and defaults its message toReceived a ${status} response.;ResponseErrorOptionsis extracted from the old malformed options. MalformedResponseErrorextendsResponseError(packages/client/src/error.ts:204) — drops its duplicatedresponsefield while preserving message inference andname.- RPC and OpenAPI link codecs (
rpc-link-codec.ts:147,openapi-link-codec.ts:410) — attach aResponseErrorascausewhen the error body is anORPCError-shaped JSON; the OpenAPIcustomErrorResponseBodyDecoderpath still returns its error as-is. - Docs + tests — new "Error Responses" sections in both link docs (anchors match the JSDoc
@seelinks); tests assert thecausechain and that it is a plainResponseError, not aMalformedResponseError.
Verified locally: the affected tests pass and @orpc/client / @orpc/openapi type-check cleanly. No framework source branches on the absence of error.cause, so attaching one is non-breaking for existing consumers.
deepseek-v4.1-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-lock
@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: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Merging this PR will not alter performance
|
## Summary `RetryLinkPlugin` no longer retries errors from a 4xx response by default, except `408` and `429`, since retrying them would most likely fail the same way. Everything else is still retried: network failures, 5xx responses, and errors thrown mid-stream by an AsyncIteratorObject. ## Changes - **New `isRetryableError(error)`** exported from `@orpc/client/plugins` and used as the default `shouldRetry`. It reads the status from the `ResponseError` cause of a decoded `ORPCError` (added in [#2265](#2265)), or from a `MalformedResponseError`. Custom `shouldRetry` functions can build on it. - **Status only**: whether the error is defined does not matter. A defined code missing from the handler's `errorStatusMap` is sent as `500` and is retried. - **Docs**: new "Client Error Responses" section on the retry plugin page, plus a v1 migration note. - **Tests**: a status table for `isRetryableError`, plugin tests, and an e2e test (`tests/rpc/retry.test.ts`) running a real `RPCHandler` and `RPCLink`. ## Breaking Change The default `shouldRetry` changes from `true` to `(o) => isRetryableError(o.error)`. To keep the previous behavior: ```ts new RetryLinkPlugin({ default: { shouldRetry: true } }) ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01TT8g89r9wLou1gmzXeQuaQ Co-authored-by: Claude <noreply@anthropic.com>

Summary
Introduce a new
ResponseErrorclass that carries resolved response details (status, headers, body) when links encounter error responses. This allows callers to access response metadata that isn't part of the error itself, such as status codes and headers.Changes
ResponseErrorclass: A lightweight error that wraps response details, used as thecauseofORPCErrorinstances decoded from error responsesMalformedResponseError: Now extendsResponseErrorinstead ofError, eliminating duplicate response handling logicRPCLinkCodecandOpenAPILinkCodecnow attach aResponseErroras thecausewhen decoding error responses, making response metadata available to error handlerscausechainResponseErrorand updated existing tests to verify thecausechainImplementation Details
ResponseErrorinfers a default message from the response status (e.g., "Received a 404 response.") when no custom message is providedMalformedResponseErrornow delegates toResponseError's constructor, reducing code duplicationcauserather than thrown directly, preserving the primary error (ORPCError) while keeping response details accessiblehttps://claude.ai/code/session_01RggAhK2fPDzS1W965ZaJQY