All topics
exceptions
beginner

Custom Exceptions & Chaining

Define domain-specific exception types and preserve the original cause when wrapping lower-level failures.

Generic exceptions like RuntimeException or IllegalStateException tell a caller that something failed but nothing about what, in the language of your domain. Custom exception types — InsufficientFundsException, OrderNotFoundException — turn failures into named, catchable concepts that callers can handle selectively and that read clearly in logs and stack traces. The companion technique, exception chaining, ensures that wrapping a low-level failure in a domain-level one doesn't throw away the evidence of what originally went wrong.

A receipt that says "delivery failed — caused by: truck breakdown — caused by: engine fault". Each layer adds context without losing the root cause.

Key Concepts

1
You define a custom exception by extending Exception (checked, when the caller is expected to recover) or RuntimeException (unchecked, for programming errors or when you prefer unchecked propagation), and you give it constructors — importantly one that accepts a Throwable cause. Chaining is then a matter of catch (SQLException e) { throw new OrderPersistenceException("Failed to save order " + id, e); }. Passing the original e as the cause preserves the full underlying stack trace, which prints as a "Caused by:" section beneath your exception. This lets you raise the abstraction level — a service caller deals with an OrderPersistenceException, not a raw SQLException leaking persistence details — without losing the root-cause diagnostics that make production incidents debuggable.
ExceptionRuntimeExceptionThrowable causecatch (SQLException e) { throw new OrderPersistenceException("Failed to save order " + id, e); }e
2
The points that distinguish a strong answer: always preserve the cause when wrapping (dropping e is one of the most common and costly mistakes, because it erases the real reason for the failure); don't create a sprawling hierarchy of one-off exception classes when a couple of well-chosen types with good messages suffice; carry useful context in fields (the order id, the account number) rather than only in the message string; and throw exceptions that match the abstraction level of the API the caller sees.
e