🎓 A1Learner
🔍
0 of 40 lessons completed

Java Exception Chaining

Exception chaining wraps a low-level failure in a clearer exception while keeping the original as its cause. The cause stays attached, so no detail of the failure is lost.

The chain in two lines

The low method fails with a plain state message. The middle method wraps it, and the top prints both messages.

public class Main {
  static void store() {
    throw new IllegalStateException("no space");
  }

  static void save() throws SaveException {
    try {
      store();
    } catch (IllegalStateException e) {
      throw new SaveException("could not save the file", e);
    }
  }

  public static void main(String[] args) {
    try {
      save();
    } catch (SaveException e) {
      System.out.println(e.getMessage());
      System.out.println(e.getCause().getMessage());
    }
  }
}

class SaveException extends Exception {
  SaveException(String message, Throwable cause) {
    super(message, cause);
  }
}

Two wraps deep

A second layer wraps the wrapper in the same way. Three prints walk the chain from the outside to the first failure.

public class Main {
  static void store() {
    throw new IllegalStateException("no space");
  }

  static void save() throws SaveException {
    try {
      store();
    } catch (IllegalStateException e) {
      throw new SaveException("could not save the file", e);
    }
  }

  static void backup() throws SaveException {
    try {
      save();
    } catch (SaveException e) {
      throw new SaveException("could not back up the file", e);
    }
  }

  public static void main(String[] args) {
    try {
      backup();
    } catch (SaveException e) {
      System.out.println(e.getMessage());
      System.out.println(e.getCause().getMessage());
      System.out.println(e.getCause().getCause().getMessage());
    }
  }
}

class SaveException extends Exception {
  SaveException(String message, Throwable cause) {
    super(message, cause);
  }
}

Example

public class Main {
  static void connect() {
    throw new IllegalStateException("no connection");
  }

  static void fetch() throws FetchException {
    try {
      connect();
    } catch (IllegalStateException e) {
      throw new FetchException("could not fetch the page", e);
    }
  }

  public static void main(String[] args) {
    try {
      fetch();
    } catch (FetchException e) {
      System.out.println(e.getMessage());
      System.out.println(e.getCause().getMessage());
      System.out.println(e.getCause().getClass().getSimpleName());
    }
  }
}

class FetchException extends Exception {
  FetchException(String message, Throwable cause) {
    super(message, cause);
  }
}

Key points

  • A wrapper exception takes the original exception in its constructor and passes it to super.
  • The getCause method answers the original exception.
  • Each layer can add context while the first failure stays at the bottom of the chain.
  • The printStackTrace method prints the whole chain, the wrapper first and each cause below it.
  • Chaining reports the failure in the terms of the layer that caught it, without losing the layer below.

In this example, the fetch layer wraps a connection failure, and the top prints the wrapper message, the cause message, and the cause type name.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

How does a wrapper exception keep the original exception?