When an exception gets lost

April 25, 2013 | 3 min Read

Image via CC from Paul Gorbould

Recently I fell into the lost exception pitfall - when an exception thrown in a try block gets lost because another exception is thrown in the finally block. It wasn’t that I was not aware of the problem, but rather that I underestimated and therefore ignored it. This post describes an example where suppressed exceptions have fatal consequences, and describes possible solutions, one of them using Java 7’s try-with-resources.

The trap

Our web application is based on the Eclipse Remote Application Platform (RAP). The main loop of the session thread is wrapped into a try block, cleanup is done in the finally block. When the session times out, a java.lang.Error (more precisely, a java.lang.TreadDeath) is thrown from within the while loop:

try {
   // main loop
   while (!done) {
      // when the session times out, an error is thrown here
      ...
   }
} finally {
   // there is a bug in this method, causing a 
   // NullPointerException to be thrown
   cleanup();
}

Unfortunately the cleanup method was programmed sloppily and sometimes causes a NullPointerException. In that case the Error is swallowed and the NullPointerException is propagated instead.

In our case this leads to the situation where the session cannot be terminated correctly, resulting in a memory leak. This effect is similar to catching Throwable in application code as described in Ivan Furnadjiev’s post. Our case is even worse since the error is swallowed without a trace - when Throwable is caught, there is at least a slim chance of finding it in a log.

The situation described above is an example of a well-known problem in Java called suppressed, lost or masked exception.

Solution 1: Handle exceptions in the finally block and never let them out

Proposed for example, in the Exception-Handling Antipatterns, this solution handles exceptions in the finally block but never propagates them. The exception thrown in the try block is the only one visible to the outside. For our use case, logging the exceptions is a proper handling:

} finally {
   try {
      cleanup();
   } catch (Exception ex) {
      log.error(ex);
   }
}

This solution is easy and quite readable, but the exceptions in the finally block are not available anymore for processing outside of this scope.

Solution 2: Collect all exceptions in a wrapper exception

The idea is to collect all exceptions occurring in the try and finally blocks, and to attach them to a single exception which is thrown in the end. This article shows several code snippets on how to achieve that. This solution preserves all exceptions, but if you look at the article, you can see that the code quickly becomes unreadable and therefore unmaintainable.

Solution 3: Java 7 to the rescue

The try-with-resources statement introduced in Java 7 preserves suppressed exceptions and has a short and readable syntax. Exceptions formerly thrown in the finally block are automatically attached as suppressed exceptions to the Throwable thrown in the try block. The cleanup code has to be wrapped into the close method of a class which implements the AutoCloseable interface:

class Cleanup implements AutoCloseable {

    @Override
    public void close() throws Exception {
        cleanup();
    }
}

...

try (Cleanup cleanup = new Cleanup()) {
   while (!done) {
      ...
   }
}

The Error is now propagated properly, with the NullPointerException as a suppressed exception. Note that this is a simplified version of the close method - it should not suppress Exception in general but rather filter out relevant ones (see the JavaDoc of the close method for details).

If the Java 6 end of life did not convince you to migrate to Java 7, maybe the try-with-resources will.