BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Errors and logging

Errors and logging

Exception causes and stack traces

When one exception is caused by another, pass the original as the "cause": throw new ServiceException("save failed", e).

Learn these first

At a glance

Mental model

A chain of notes at a hospital: "patient has fever, because of infection, because of a cut".

What happens, in order
  1. 1Wrap and rethrow: catch (SQLException e) { throw new RepositoryException("load " + id, e); }Layers speak their own language but keep the cause.
  2. 2Read a stack trace from the top: the first line is the outer exception, at lines show the call path (the first one is where it was created), and the last "Caused by:" is usually the real root cause
  3. 3e.getMessage() is the text; e.getCause() is the next exception in the chain; e.getStackTrace() returns the framestry-with-resources records exceptions from close() as suppressed (getSuppressed()).
  4. 4Log once, with the exception as the last argument: log.error("could not load {}", id, e)Do not log and rethrow at every layer, or one failure appears ten times.
Remember
Watch out
Wrapping without the cause (new X(e.getMessage()))Logging and rethrowing at every layerSwallowing exceptions silentlyUsing printStackTrace in server code
Example (highlighted lines explain it)
import java.sql.SQLException;public class Repo {  static class RepositoryException extends RuntimeException {    RepositoryException(String message, Throwable cause) { super(message, cause); }  }  void load(long id) {    try { query(id); }    catch (SQLException e) { throw new RepositoryException("load " + id, e); }  }  static Throwable rootCause(Throwable t) {    while (t.getCause() != null) t = t.getCause();    return t;  }  void query(long id) throws SQLException { throw new SQLException("connection reset"); }}
  1. line 4 uses Throwable, the core of Exception causes and stack traces
  2. line 10 uses Throwable, the core of Exception causes and stack traces
  3. line 11 uses getCause, the core of Exception causes and stack traces
Read the full explanation (about 2 min)

When one exception is caused by another, pass the original as the "cause": throw new ServiceException("save failed", e). The stack trace then shows both, with "Caused by:" lines leading to the root problem.

A mental model

A chain of notes at a hospital: "patient has fever, because of infection, because of a cut". Each layer adds its own explanation without throwing away the underlying finding.

How it works

Wrap and rethrow: catch (SQLException e) { throw new RepositoryException("load " + id, e); }. Layers speak their own language but keep the cause.

Read a stack trace from the top: the first line is the outer exception, at lines show the call path (the first one is where it was created), and the last "Caused by:" is usually the real root cause.

e.getMessage() is the text; e.getCause() is the next exception in the chain; e.getStackTrace() returns the frames. try-with-resources records exceptions from close() as suppressed (getSuppressed()).

Log once, with the exception as the last argument: log.error("could not load {}", id, e). Do not log and rethrow at every layer, or one failure appears ten times.

Going deeper

Swallowing exceptions (catch (Exception e) {}) is the single most damaging habit: the program continues in an unknown state and nobody learns why. At minimum log it with the stack trace.

e.printStackTrace() goes to stderr and bypasses the logging system. Use a logger.

The message should say what was being done and with what (ids, file names) and not repeat the exception class name; sensitive data (passwords, tokens) must never go into it.

Our stack trace beautifier on this site folds the framework frames and highlights the "Caused by" chain.

Under the hood

A Throwable records the stack in fillInStackTrace() when it is created (not when it is thrown), which makes exceptions comparatively expensive. The stack trace holds StackTraceElement entries (class, method, file, line) captured from the thread's frames.

Common mistakes

  • Wrapping without the cause (new X(e.getMessage())).
  • Logging and rethrowing at every layer.
  • Swallowing exceptions silently.
  • Using printStackTrace in server code.

How Unravel recognises it

When you paste code, Unravel looks for these signs of exception causes and stack traces. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Exception causes and stack traces?

When one exception is caused by another, pass the original as the "cause": throw new ServiceException("save failed", e). The stack trace then shows both, with "Caused by:" lines leading to the root problem.

What are the most common mistakes with Exception causes and stack traces?

Wrapping without the cause (new X(e.getMessage())). Logging and rethrowing at every layer. Swallowing exceptions silently. Using printStackTrace in server code.

How can I see Exception causes and stack traces in real code?

Paste your own Java into Unravel and it will point out where exception causes and stack traces appears, explain how it works at the detail level you choose, and draw the flow of the code around it.