Classify failures before choosing the UI
Not every unsuccessful result is an exception. Validation feedback, missing permissions, and an empty search are expected outcomes; broken dependencies, programmer defects, and corrupted data are operational failures. Give each class a deliberate response.
Expected
Return typed, actionable feedback beside the user's task.
Recoverable
Preserve context and offer a safe retry or alternate path.
Unexpected
Contain the fault, show a stable fallback, and emit diagnostics.
A single generic “something went wrong” path hides useful product decisions and makes production diagnosis harder.
Contain rendering failures near useful recovery points
Place boundaries around route segments or features that can fail independently. A recommendation panel should not erase checkout; an analytics chart should not blank the entire dashboard. The fallback must explain impact without exposing stack traces, tokens, database details, or private identifiers.
Return stable contracts from server code
Validate input before effects, authorize the specific resource, and map known domain failures to small response codes. Log the internal cause with a request ID while returning a safe message to the browser.
try {
const order = await placeOrder(input, actor);
return { ok: true, orderId: order.id };
} catch (error) {
logger.error({ error, requestId }, "order_failed");
return { ok: false, code: "ORDER_UNAVAILABLE", requestId };
}Do not catch an error only to discard it. Either translate it at an ownership boundary or let it reach the boundary responsible for reporting and fallback rendering.
Retry reads carefully and mutations defensively
Use short timeouts, capped backoff, and jitter for transient dependency failures. Retries amplify outages when every request repeats expensive work, and they can duplicate payments, emails, or orders unless mutations are idempotent.
- Never retry validation, authorization, or permanent not-found outcomes.
- Stop retries when the user leaves or the request deadline expires.
- Show cached or partial data only when its age and limitations are acceptable.
- Provide a reconciliation path for uncertain side effects.
For mutation protections, use the validation and authorization practices in our Server Actions security guide.
Make every incident traceable without leaking data
Attach a stable request ID to the browser response, server logs, downstream calls, and user support message. Record route template, operation, outcome, latency, dependency, and deployment version with bounded labels. Sample noisy successes, but retain enough failure context to reproduce the sequence.
Alert on user impact—failed checkouts, error-boundary views, sustained route failure, or latency—not every logged exception. Connect these signals using our request tracing and alerting guide.
Next.js error handling checklist
✓ Expected outcomes use typed feedback
✓ Boundaries preserve unaffected features
✓ Errors never expose sensitive details
✓ Server responses use stable codes
✓ Retries are capped and cancellable
✓ Mutations are safe against duplicates
✓ Request IDs join client and server evidence
✓ Alerts measure real user impact
Build a more dependable Next.js application
Endurance Softwares helps teams design, build, test, and operate production-ready Next.js platforms.
