ExecutionException
JavaERRORNotableConcurrency

Exception thrown by async task (wrapped)

Quick Answer

Always call e.getCause() when catching ExecutionException to get the actual exception; log and rethrow the cause, not the wrapper.

What this means

Thrown by Future.get() and CompletableFuture.get() when the underlying asynchronous task threw an exception. The original exception is wrapped — access it with getCause().

Why it happens
  1. 1The Callable or Runnable submitted to an ExecutorService threw an exception
  2. 2A CompletableFuture stage threw an unchecked exception

Fix

Unwrap the cause and rethrow

Unwrap the cause and rethrow
try {
    String result = future.get();
} catch (ExecutionException e) {
    Throwable cause = e.getCause();
    if (cause instanceof MyDomainException mde) {
        throw mde;  // rethrow typed
    }
    throw new RuntimeException("Async task failed", cause);
}

Why this works

getCause() retrieves the original exception. Pattern matching instanceof (Java 16+) allows typed unwrapping without a chain of instanceof checks.

Code examples
CompletableFuture handle()java
future.handle((result, ex) -> {
    if (ex != null) {
        log.error("Task failed", ex);  // ex is already unwrapped
        return fallback;
    }
    return result;
});
Sources
Official documentation ↗

Java SE Documentation

Content generated with AI assistance and reviewed for accuracy. Found an error? hello@errcodes.dev

← All Java errors