Error Handling
In one line: Every error follows one structured JSON shape with a
machine-readable
code, a human message, and a requestId for Sentry
correlation. Operational (expected) errors are logged at warn; programmer
(unexpected) errors are logged at error and alert via Sentry.Design Principles
- Never leak internals — No stack traces, DB column names, or internal IDs in client responses
- Structured, predictable responses — Every error follows the same JSON shape
- Machine-readable codes — Clients can programmatically handle specific errors
- Human-readable messages — Users see meaningful text, not “Something went wrong”
- Correlation everywhere — Every error carries a
requestIdfor Sentry cross-referencing - Fail gracefully — Operational errors return proper HTTP codes; programmer errors trigger alerts
Standard Response Shapes
- Tab Title
- Tab Title
HTTP Status Code Map
Error Classification
- Tab Title
- Tab Title
Logged at
warn level. No Sentry alert.Error Code Registry
All error codes are centralised inpackages/errors/codes.ts — no magic strings:
AppError Class
API Route Pattern
All API routes are wrapped withwithErrorHandler:
Business Layer Pattern
Business logic throws AppError — never catches it. The handler layer above decides what to do:Request ID Correlation
Every request gets a unique ID attached in middleware:Sentry Integration
Rate Limiting
All rate limiting runs in Next.js middleware via@upstash/ratelimit backed by Upstash Redis. Applied before auth checks or any business logic.
Client-Side Error Handling
The client switches on the errorcode to decide what the user sees:
React Error Boundaries catch rendering errors at the route level. Each route group has its own error.tsx that reports to Sentry and shows a retry button.