Error Messages & Standardized Error Envelopes
The 3-Line Engineering Rule
Return consistent, structured error envelopes (RFC 7807 Problem Details); never leak raw stack traces or SQL exceptions to API clients.
Raw stack traces expose internal class names, database schemas, and library versions to attackers, while inconsistent error shapes break frontend error handling.
Standardize on RFC 7807: include `type`, `title`, `status`, `detail`, `instance`, and an opaque `error_id` (trace ID) so users can report issues without seeing raw code internals.
Architectural Blueprint & Sequence
Hover over nodes to inspect state transitions❌ RAW EXCEPTION LEAK
HTTP 500: PSQLException: relation 'users' does not exist at org.postgresql.core...
✅ RFC 7807 PROBLEM DETAILS
{ status: 422, title: 'Unprocessable Entity', detail: 'Insufficient stock', error_id: 'err_8f9a' }
Customer Support / Triage
Look up error_id in Loki/Elasticsearch
Key Engineering Takeaways
- Adopt RFC 7807 'Problem Details for HTTP APIs' across all services.
- Always attach an `error_id` / `trace_id` so frontend users can quote it to customer support.
- Never leak SQL syntax errors, database passwords, or full Java stack traces to API clients.
- Distinguish client errors (4xx - invalid input) from internal system defects (5xx - server crash).
The Problem & Production Reality
When an API call fails, the client needs to know two things: What went wrong? How can I fix it? Returning inconsistent string messages like `"Something broke"` on one endpoint and an HTML stack trace on another creates a frustrating developer experience and leaks attack vectors.
Anti-Pattern: Default Framework Error Pages & Leaked Stack Traces
Allowing uncaught exceptions to bubble up to Tomcat or Express, returning raw database traces to the browser.
// ❌ ANTI-PATTERN: Leaks database table names and credentials
HTTP/1.1 500 Internal Server Error
Content-Type: text/plain
org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "users_email_key"
Detail: Key (email)=(john@example.com) already exists.
at org.hibernate.exception.internal.SQLStateConverter...Production Pattern: RFC 7807 ProblemDetail Global Handler
Use Spring's native `ProblemDetail` or custom RFC 7807 envelope with sanitized client messages and an opaque trace ID.
// ✅ PRODUCTION PATTERN: RFC 7807 Global Exception Handling
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(InsufficientInventoryException.class)
public ProblemDetail handleInventoryError(InsufficientInventoryException ex, HttpServletRequest req) {
String errorId = MDC.get("traceId");
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
HttpStatus.UNPROCESSABLE_ENTITY,
ex.getMessage() // Safe domain message: "Only 2 items remain in stock."
);
problem.setTitle("Inventory Shortage");
problem.setType(URI.create("https://beingsde.in/errors/insufficient-inventory"));
problem.setInstance(URI.create(req.getRequestURI()));
problem.setProperty("error_id", errorId);
problem.setProperty("available_quantity", ex.getAvailableQuantity());
problem.setProperty("timestamp", Instant.now());
return problem;
}
@ExceptionHandler(Exception.class)
public ProblemDetail handleUnexpectedCrash(Exception ex, HttpServletRequest req) {
String errorId = UUID.randomUUID().toString().substring(0, 8);
log.error("Unhandled internal server crash: error_id={}", errorId, ex);
// NEVER leak ex.getMessage() for unexpected 500s!
ProblemDetail problem = ProblemDetail.forStatusAndDetail(
HttpStatus.INTERNAL_SERVER_ERROR,
"An unexpected server error occurred. Please quote error_id to support."
);
problem.setTitle("Internal Server Error");
problem.setProperty("error_id", errorId);
return problem;
}
}Trade-off Analysis & Staff Recommendations
| Aspect | Pros / Strengths | Cons / Hazards | Staff Recommendation |
|---|---|---|---|
| Custom JSON vs RFC 7807 Problem Details | RFC 7807 is an open IETF internet standard natively supported by modern frameworks. | Requires migrating legacy frontend parsers to expect the standard schema. | Adopt RFC 7807 for all new microservices; it is standard in Spring Boot 3+ and .NET 7+. |
| Developer Details in Staging vs Prod | Showing stack traces in local dev speeds up debugging. | Accidentally leaving dev mode on in production causes security breaches. | Completely disable stack trace inclusion in HTTP responses at the build/profile level. |