Catching Java Assertion Errors

The Java assert statement can be used for conditionally checking program invariants. Assertions are enabled or disabled with the desired level of granularity by Java Virtual Machine flags, as described in the linked guide. So far, so similar to assertion facilities in other languages.

However, whereas .NET Debug/Trace.Assert shows and logs a message by default, and C++ assert unconditionally terminates the program, Java assertion failures throw an AssertionError.

You might think that would be an Exception. Even the Oracle guide linked at the top sloppily speaks of “uncaught exceptions” from assertion failures. Well, this is wrong.

Quick Demonstration

First, a small sample program to show how to catch (or not) Java assertion failures. Compile the following program and run it with java -ea to enable assertions.

public class ThrowableTest {
    public static void main(String[] args) {
        int x = 0;
        try {
            assert(x > 0); 
        }
        catch (Exception e) {
            System.out.println("Exception caught");
        }
    }
}

This does not catch the assertion failure but rather terminates the program with an error message and stack trace. Lamentably the message begins with “Exception in thread “main”…” so as to maximize programmer confusion. The error was a Throwable, not an Exception. Here is the corrected version that will catch the assertion failure:

public class ThrowableTest {
    public static void main(String[] args) {
        int x = 0;
        try {
            assert(x > 0); 
        }
        catch (Throwable e) {
            System.out.println("Throwable caught");
        }
    }
}

Throwable vs. Exception

Aside from its notorious checked exceptions, another peculiarity of Java’s runtime error API as compared to .NET is that Exception is not the base class for all errors – Throwable is. Exception is one of two subclasses of Throwable. The other is Error.

As per its documentation, Error represents “serious problems” and “abnormal conditions” that a “reasonable application should not try to catch.” For the most part that is undoubtedly true (e.g. VirtualMachineError) but there is one glaring exception (sorry): AssertionError.

What are assertion failures doing in this company? Should they not result in some kind of RuntimeException, the base class for all other types used to check method arguments and object states? Under The AssertionError Class, Oracle explains that “this issue was controversial” but eventually decided in favor of a C++-style interpretation where assertion failures should generally just terminate the program and not be recovered from.

Background Threads

You may or may not find this reasoning convincing, but there is one important case that the Java expert team seems to have ignored: background threads. When an assertion failure occurs in a background thread, it is by default neither reported nor aborts the program. It just quietly terminates the thread (or the current operation in a thread pool), leaving a programmer who catches only the Exception class – as advised by Oracle! – scratching his head and wondering whatever happened.

This was in fact how I stumbled on the curious nature of AssertionError. Eventually I figured out that the top-level caller on my background thread had to use a try/catch block with Throwable rather than Exception. Then the assertion errors were correctly caught and reported. Presumably for the same reason, Java’s own Thread.UncaughtExceptionHandler facility also reports any Throwable, and not just Exception as one might infer from the class name.

One thought on “Catching Java Assertion Errors

  1. Pingback: Catching Java Assertion Errors – Full-Stack Feed

Leave a Reply