How can I pass a collection of exceptions as a root cause?

I'm not sure I'd do it (though given the JavaDoc I couldn't tell you why I hesitate), but there is the list of suppressed exceptions on Throwable, which you can add to via addSuppressed. The JavaDoc doesn't seem to say this is only for the JVM to use in try-with-resources:

Appends the specified exception to the exceptions that were suppressed in order to deliver this exception. This method is thread-safe and typically called (automatically and implicitly) by the try-with-resources statement.

The suppression behavior is enabled unless disabled via a constructor. When suppression is disabled, this method does nothing other than to validate its argument.

Note that when one exception causes another exception, the first exception is usually caught and then the second exception is thrown in response. In other words, there is a causal connection between the two exceptions. In contrast, there are situations where two independent exceptions can be thrown in sibling code blocks, in particular in the try block of a try-with-resources statement and the compiler-generated finally block which closes the resource. In these situations, only one of the thrown exceptions can be propagated. In the try-with-resources statement, when there are two such exceptions, the exception originating from the try block is propagated and the exception from the finally block is added to the list of exceptions suppressed by the exception from the try block. As an exception unwinds the stack, it can accumulate multiple suppressed exceptions.

An exception may have suppressed exceptions while also being caused by another exception. Whether or not an exception has a cause is semantically known at the time of its creation, unlike whether or not an exception will suppress other exceptions which is typically only determined after an exception is thrown.

Note that programmer written code is also able to take advantage of calling this method in situations where there are multiple sibling exceptions and only one can be propagated.

Note that last paragraph, which seems to suit your case.


Exceptions and their causes are always only a 1:1 thing: you can throw one exception and each exception can only have one cause (which can again have one cause ...).

That could be considered a design fault, especially when considering multi-threaded behaviour as you described.

That's one of the reasons why Java 7 added addSuppressed to throwable which can basically attach an arbitrary amount of exceptions to a single other one (the other primary motivation was try-with-resources which needed a way to handle exceptions in the finally block without silently dropping them).

So basically when you have 1 exception that causes your process to fail, you add that one as the cause of your higher-level exception, and if you have any more, then you add those to the original one using addSuppressed. The idea is that that first exception "supressed" the others becoming a member of the "real exception chain".

Sample code:

Exception exception = null;
for (Foobar foobar : foobars) {
  try {
    foobar.frobnicate();
  } catch (Exception ex) {
    if (exception == null) {
      exception = ex;
    } else {
      exception.addSuppressed(ex);
    }
  }
}
if (exception != null) {
  throw new SomethingWentWrongException(exception);
}