You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(api): document declared error shapes across public oRPC routes (#1109)
* feat(api): document declared error shapes across public oRPC routes
Attach commonApiErrors (401/403/422/429/500 + workspace-access denials)
once to the shared public API stacks in orpc.ts, so every public
procedure inherits a consistent OpenAPI error contract without a
per-router .errors() call. Remap oRPC's own BAD_REQUEST validation
throw to 422 so schema-level and business-level validation failures
share one status, and align validationException to 422 to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VWhmQGALFoH4NVXMsXypA
* chore: fix revie
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
### Declared errors (`.errors()`) — public routes only
392
+
393
+
Every public procedure must declare the errors it can throw, because that
394
+
declaration is what renders the non-2xx responses in the OpenAPI spec (and
395
+
what the MCP server and CLI show a caller). The declaration is split in two:
396
+
397
+
| Layer | Where | Contains |
398
+
|-------|-------|----------|
399
+
| Shared |`commonApiErrors`, attached **once** to the public stacks in `@/orpc`| 401 (`UNAUTHORIZED`, `INVALID_CHATBOT_TOKEN`), 403 (`FORBIDDEN`, `trialExpired`, `macLimitReached`), 422 (`invalidRequestData`, `validation`), 429 (`tooManyRequests`), 500 (`INTERNAL_SERVER_ERROR`) |
400
+
| Per-route | one `possibleErrorsOn*Resource` set from `@/lib/orpc/orpc-error-helper`| only what varies by operation shape — `notFound` (404) and `businessError` (400) |
401
+
402
+
Pick the per-route set by what the handler can actually fail with, not by the
403
+
HTTP verb:
404
+
405
+
-`possibleErrorsOnListingResource` — a collection read that cannot 404.
406
+
-`possibleErrorsOnFindingResource` — a read that resolves one resource.
407
+
-`possibleErrorsOnCreatingResource` — a create with no parent lookup.
408
+
-`possibleErrorsOnMutatingResource` — an update, **or a create that resolves a
409
+
parent from a path param** (e.g. `POST /v1/contacts/{identifier}/notes` calls
410
+
`contactService.resolveIdByIdentifier`, which throws 404).
411
+
-`possibleErrorsOnDeletingResource` — a delete.
412
+
413
+
**Never re-declare a `commonApiErrors` code in a per-route set.** Doing so
414
+
duplicates the entry in the generated spec. The guard in
415
+
`apps/builder/__tests__/public-spec-operations.test.ts` fails on both mistakes:
416
+
a route missing a universal code, and a duplicated one.
417
+
418
+
**The `code` string is the contract, not the status.** oRPC matches a thrown
419
+
`ORPCError` to its declaration by `code`*and* exact `status`
420
+
(`validateORPCError` in `@orpc/contract`). On a miss it does not error — it
421
+
returns the error with `defined: false`, so an undeclared code still reaches
422
+
the client but never appears in the spec. That silent degrade is why a new
423
+
`ChatbotXException` code thrown from a public route needs a matching entry in
424
+
one of these sets.
425
+
388
426
## Logging
389
427
390
428
Import the logger from the nearest `lib/log` or `lib/logger` module. Never use `console` in handlers.
0 commit comments