Java Interview Questions on CompletableFuture Failures
CompletableFuture lets you compose asynchronous results, but you still need to choose how failures and execution resources are handled. An interview answer should explain the shape of the dependency: does the second operation depend on the first result, or can two results be combined independently?
By Ravindra Wadile · Published

576 words · About 3 min read
CompletableFuture lets you compose asynchronous results, but you still need to choose how failures and execution resources are handled. An interview answer should explain the shape of the dependency: does the second operation depend on the first result, or can two results be combined independently?
The Java 21 example uses already completed futures so the failure path is deterministic. It makes no network call and needs assertions enabled.
How can two results be combined?
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
class Example {
public static void main(String[] args) {
var region = CompletableFuture.completedFuture("north");
var count = CompletableFuture.completedFuture(3);
assert "north:3".equals(region.thenCombine(count,
(name, value) -> name + ":" + value).join());
CompletableFuture<Integer> failed = new CompletableFuture<>();
failed.completeExceptionally(new IllegalStateException("sample failure"));
var combined = region.thenCombine(failed,
(name, value) -> name + ":" + value);
try {
combined.join();
throw new AssertionError("failure was hidden");
} catch (CompletionException expected) {
assert expected.getCause() instanceof IllegalStateException;
}
assert failed.exceptionally(error -> 0).join() == 0;
}
}
The combine function needs both successful values. If an input fails, the composed result is exceptional rather than a normal region/count string. The recovery expression is a separate choice that converts a failure to a fallback value.
When is thenCompose the clearer choice?
Use composition when the next operation produces another future and depends on the earlier result. It avoids treating a future-containing-future as if it were the final value. Combining independent results expresses a different dependency.
Describe the actual workflow. “Find the account, then request its settings” differs from “request two independent public summaries, then combine them.” Similar-looking callback syntax should not hide those different requirements.
Should every exception become a default value?
No. A fallback is valid only when the caller can safely use it. Returning zero for a failed lookup can be misleading if zero means a verified count. It may be better to preserve a failure or return an explicitly partial result.
The example uses zero only to demonstrate exceptionally. It is not a recommendation to substitute missing business data silently. An interview answer should state what the caller can still trust after recovery.
Where do callbacks execute?
Non-async and async variants have documented execution rules. Async variants without a supplied executor commonly use the implementation's default asynchronous facility; supplying an executor gives a different resource boundary. Do not promise a fresh dedicated thread for every stage.
Blocking operations, constrained executors and callback dependencies can create problems even when each method name contains Future. Discuss resource ownership and avoid blocking waits inside work that occupies the only resource needed for progress.
Does cancellation always interrupt underlying work?
No. CompletableFuture's cancellation contract is not a universal interruption mechanism for every operation that may complete it. An external task or network request can have its own cancellation API and side effects.
Likewise, a timeout on waiting does not prove a remote write failed to happen. Separate the future's completion state from the external action's outcome.
How should failure tests be designed?
- Complete one dependency successfully and another exceptionally.
- Check the caller's exceptional result, not only a log message.
- Test any intentional recovery independently.
- Verify whether unfinished underlying work needs separate cleanup.
For practice, replace the independent count with a future-producing lookup that needs the region string. Explain why that changes the composition operator. A clear dependency diagram is more useful than reciting every method in the API.
References and next practice
java question collection · Browse interview topics · Practise a question in Interview Challenge · How to Answer Technical Interview Questions Out Loud