Can you rethrow an exception?
Rethrow - catching an exception in catch to perform an action (logging) and sending it further:
Junior Level
Yes, you can!
Rethrow - catching an exception in catch to perform an action (logging) and sending it further:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
public void processOrder(Long orderId) {
try {
orderRepository.save(orderId);
} catch (Exception e) {
log.error("Failed to process order id={}", orderId, e); // Log
throw e; // Propagate further
}
}
}
Why it’s needed
- Logging - record the error before it goes further
- Metrics - count the number of errors
- Cleanup - release resources before propagation
try {
connection.beginTransaction();
connection.save(order);
} catch (SQLException e) {
connection.rollback(); // Rollback transaction
throw e; // Propagate further
}
Wrapping on rethrow
Often wrapped in another exception:
try {
repository.save(order);
} catch (SQLException e) {
throw new OrderException("Failed to save order id=" + order.getId(), e); // New type + context
}
When NOT to use rethrow
- Without adding value -
catch (Exception e) { throw e; }is useless without logging/metrics - Duplicate logging - if exception is already logged at this level, don’t log again on rethrow
- Instead of wrapping - if you need a new exception type, use wrapping with cause, not bare rethrow
- In transactions without rollback - rethrow of unchecked exception in
@Transactionalrolls back transaction, checked - doesn’t - Multiple rethrow - don’t catch/throw the same exception at every layer - one level is enough
Middle Level
Instruction athrow
At the bytecode level, rethrow is a regular athrow instruction. It takes the exception object from the operand stack and searches for a handler in the Exception Table.
Important: on throw e; the stack trace doesn’t change. It shows the original location of occurrence, not the throw e line.
Precise Rethrow (Java 7+)
One of the most useful but rarely discussed features:
public void process() throws IOException, SQLException {
try {
if (condition) throw new IOException();
else throw new SQLException();
} catch (Exception e) { // Catch common ancestor
log.error("Intermediary processing");
throw e; // Compiler "knows" - only IOException or SQLException
}
}
Before Java 7, you’d have to declare throws Exception. Now the compiler analyzes the contents of try.
Rethrow with wrapping (Exception Chaining)
// Plus: add context of the current layer
throw new MyException("Context: processing order " + orderId, e);
// Minus: new object + new fillInStackTrace()
// Always pass the original exception as cause!
Side Effects in rethrow
In high-load systems, rethrow is used for metrics:
catch (RuntimeException e) {
metrics.increment("errors.count");
throw e;
}
Senior Level
Double Traversal
Rethrowing forces JVM to rescan the Exception Table. In deep hierarchies, frequent rethrows create a small CPU load.
Checked to Unchecked
Often checked exceptions are propagated as unchecked - “breaks” obligations in the signature:
try {
// checked exception
Files.readAllLines(path);
} catch (IOException e) {
throw new RuntimeException(e); // Unchecked - method no longer declares throws
}
Sneaky Throws
Trick with propagating checked exception without declaring in throws:
@SuppressWarnings("unchecked")
private static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
throw (E) e;
}
Lombok @SneakyThrows uses this same mechanism.
Losing Exceptions
If the catch block called a method that itself threw an exception, and you didn’t propagate the original - the original is lost:
catch (Exception e) {
log.info("Error"); // OK
doSomethingThatAlsoThrows(); // New exception! Original e is lost!
throw e; // Never executes
}
fillInStackTrace() - updating the stack trace
If you want to update the stack trace to the current line (so logs show the rethrow location):
throw (RuntimeException) e.fillInStackTrace();
Only do this if you understand why - the original stack trace will be lost.
Diagnostics
- Log Analysis - make sure each rethrow is logged with context
- Metrics - count rethrows via Micrometer
- Debugger - in IntelliJ expand
causenode to see the entire chain javap -c- shows theathrowinstruction in bytecode
Interview Cheat Sheet
Must know:
- Rethrow - catching exception in
catchwith action (log, metrics, rollback) and propagating further viathrow e - On
throw e;stack trace doesn’t change - shows original error location - Checked exceptions can be propagated without losing typing thanks to Precise Rethrow (Java 7+)
- Wrapping on rethrow adds context:
throw new OrderException("msg", e)- always passcause - Rethrow of unchecked in
@Transactionalrolls back transaction, checked - doesn’t - Useless rethrow:
catch (Exception e) { throw e; }without logging/metrics/cleanup e.fillInStackTrace()updates stack trace - use consciously, original stack will be lost
Frequent follow-up questions:
- Does stack trace change on
throw e? - No, shows original error location - What is Precise Rethrow? - Java 7+: compiler analyzes
tryand preserves exact types inthrows - When is rethrow useless? - Without logging, metrics, cleanup - bare
catch { throw e; } - How to update stack trace? -
e.fillInStackTrace()- but original stack will be lost
Red flags (NOT to say):
- “On rethrow stack trace is updated” - no, only on
fillInStackTrace() - “Rethrow of checked exception doesn’t rollback
@Transactional” - depends on type: unchecked rolls back, checked - doesn’t - “Multiple rethrows at every layer is good practice” - one level is enough
- “Rethrow without cause loses original error” - on
throw ewithout wrapper, error is not lost
Related topics:
- [[18. What is exception wrapping (wrapping)]] - wrapping on rethrow
- [[20. What does the throws keyword do]] - Precise Rethrow preserves types
- [[28. What is exception chaining]] - chaining on wrapping with cause
- [[21. Can you throw a checked exception from a method without throws]] - sneaky throws alternative
- [[17. How to properly log exceptions]] - logging before rethrow