exceptionally only runs when the upstream future completed with an exception, producing a substitute value so the chain can still complete normally. If the future had succeeded, exceptionally would simply be skipped and the original value kept.
CompletableFuture<Integer> risky = CompletableFuture.supplyAsync(() -> {
throw new ArithmeticException("bad math");
});
CompletableFuture<Integer> recovered = risky.exceptionally(ex -> -1);
System.out.println("Recovered value: " + recovered.get());
Recovered value: -1
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