🧯 B7 · Exception Handling: @RestControllerAdvice

B7 · Exception Handling: @RestController: Exception handling is the API's **fire brigade and translator crew**: when something goes wrong inside the code (record missing, rule vi

Exception handling is the API's **fire brigade and translator crew**: when something goes wrong inside the code (record missing, rule violation, unexpected crash), it TRANSLATES the raw Java exception into a clean HTTP response the client understands. `@RestControllerAdvice` is the single central error translator for all controllers: `BugNotFoundException` → 404, `IllegalStateException` → 409, everything else → 500. But couldn't we write try-catch in each method — why a central structure? Because if error translation is scattered across controllers, one returns 404, another 500, another catches nothing, and the client meets inconsistent responses; the central advice is the single source of truth for the error→status mapping. In Java the equivalent is not a global `try-catch` but `@ExceptionHandler` methods; each exception type maps to its own status code and body. In QA this layer is the **contract of error responses**: a tester must test not just the "happy path" but that error cases return the right status + a meaningful message — because a bad error response (200 instead of 500, or a leaked stack trace) both misleads the client and becomes a security hole.

The Central Error Translator

**🐞 Defect Birth — if a raw exception leaks to the client** **Code:** `@RestControllerAdvice` is MISSING, or a generic handler catches all exceptions and dumps `ex.getMessage()` as-is into a 500 body. **What happens:** a `SQLException` or `NullPointerException` returns to the client raw: the response body shows the full **stack trace**, database table/column names, even file paths. For a malicious actor this is a treasure map (it exposes internals); the client also gets no clear "what happened" message. **Why sneaky:** it never shows in happy-path tests — everything returns 200/201. The leak appears only when an error is triggered, and most teams never test error responses. **Where the tester catches it:** by deliberately triggering errors (nonexistent id, invalid data, malformed JSON) and inspecting the response body — seeing a stack trace, SQL, or internal path is both an information leak (security) and a bad error contract.

🎬 404, 500, or a Leak? The Translation of an Error

@RestControllerAdvice

An exception was thrown inside the code: a nonexistent bug was requested (BugNotFoundException). How will this raw error be returned to the client?

If the central advice EXISTS: the exception is caught and translated into a clean 404 + meaningful message. The client gets a clear "record not found" response.

If the advice is MISSING: the raw exception leaks to the client — the response body shows the full stack trace, SQL, table names, file paths.

This leak is a map for a malicious actor: it exposes internals (DB schema, tech, paths) and eases the next attack.

The lesson — Error responses are a contract too: the right status + a meaningful message, WITHOUT leaking raw internals. The tester triggers errors deliberately and audits the body.

Error → Status Mapping

BugNotFoundException is an expected case; the advice maps it to 404, not 500.

Rule violation → 409…

A conflict like already CLOSED maps to 409 Conflict — the client understands a "state mismatch".