Unlike Future.get, CompletableFuture.join throws no checked exception; if the stage completed exceptionally it throws an unchecked CompletionException whose getCause is the original failure from inside the stage.
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
throw new IllegalStateException("stage failed");
});
try {
future.join();
} catch (CompletionException e) {
System.out.println(e.getClass().getSimpleName() + " caused by " + e.getCause().getClass().getSimpleName() + ": " + e.getCause().getMessage());
}
CompletionException caused by IllegalStateException: stage failed
Run this yourself in the Online Java Compiler, spin up a live REST API in the API Sandbox, or practise with Java interview questions.
Published 2026-09-27