Error Handling and Async HTTP Pipeline
Handle errors with exceptionally, handle, and whenComplete, then build an async HTTP client pipeline.
Error Handling and Async HTTP Pipeline is a free Java Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Async Error Handling Matters
In async pipelines, exceptions don't propagate the same way as in synchronous code. You must explicitly handle errors using exceptionally, handle, or whenComplete.
exceptionally: Recovering From Errors
exceptionally(Function<Throwable, T>) is called only if the future completes with an exception. It provides a fallback value, keeping the pipeline alive.
CompletableFuture<String> safe = fetchUser(id)
.thenApply(User::getName)
.exceptionally(ex -> {
log.warn("Fetch failed: " + ex.getMessage());
return "Anonymous";
});
System.out.println(safe.join()); // "Anonymous" if fetch failedhandle: Transform Success and Failure
handle(BiFunction<T, Throwable, U>) is called for both success and failure. One of the two arguments will be null.
fetchUser(id).handle((user, ex) -> {
if (ex != null) return UserDto.empty();
return UserDto.from(user);
}).thenAccept(dto -> sendResponse(dto));whenComplete: Side Effect After Any Outcome
whenComplete(BiConsumer) is called after completion regardless of outcome. It does not change the result — use it for logging, metrics, or cleanup.
fetchUser(id)
.whenComplete((user, ex) -> {
if (ex != null) metrics.increment("user.fetch.error");
else metrics.increment("user.fetch.success");
})
.thenAccept(u -> sendResponse(u));Retrying Failed Futures
Implement retry logic by recursively calling the operation on failure up to a maximum number of attempts.
CompletableFuture<String> withRetry(Supplier<CompletableFuture<String>> task, int retries) {
return task.get().exceptionallyCompose(ex -> {
if (retries > 0) {
System.out.println("Retrying... " + retries);
return withRetry(task, retries - 1);
}
return CompletableFuture.failedFuture(ex);
});
}Java 11 HttpClient: Async GET
Java 11's HttpClient provides fully async HTTP via sendAsync(), returning a CompletableFuture<HttpResponse<String>>.
HttpClient client = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/1"))
.build();
CompletableFuture<String> body = client
.sendAsync(req, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);Chaining Multiple HTTP Calls
Use thenCompose to chain dependent HTTP calls: first fetch the user, then use the user's ID to fetch their orders.
client.sendAsync(userReq, HttpResponse.BodyHandlers.ofString())
.thenApply(r -> parseUser(r.body()))
.thenCompose(user -> client.sendAsync(
buildOrdersRequest(user.getId()),
HttpResponse.BodyHandlers.ofString()))
.thenApply(r -> parseOrders(r.body()))
.thenAccept(orders -> System.out.println("Orders: " + orders.size()));Parallel HTTP Calls with allOf
Fan out to several endpoints in parallel, then collect all responses when all complete.
List<URI> endpoints = List.of(uri1, uri2, uri3);
List<CompletableFuture<String>> requests = endpoints.stream()
.map(uri -> client.sendAsync(
HttpRequest.newBuilder(uri).build(),
HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body))
.collect(Collectors.toList());
List<String> responses = CompletableFuture
.allOf(requests.toArray(new CompletableFuture[0]))
.thenApply(v -> requests.stream().map(CompletableFuture::join).collect(Collectors.toList()))
.join();Timeout on HTTP Calls
Apply orTimeout directly to the async HTTP call to cancel it if the server takes too long.
CompletableFuture<String> result = client
.sendAsync(req, HttpResponse.BodyHandlers.ofString())
.orTimeout(5, TimeUnit.SECONDS)
.thenApply(HttpResponse::body)
.exceptionally(ex -> "timeout or error");Status Code Validation in the Pipeline
Use thenApply to check the HTTP status code and throw a domain exception if it indicates an error, keeping the pipeline's error handling consistent.
.thenApply(response -> {
if (response.statusCode() != 200)
throw new HttpResponseException(response.statusCode());
return response.body();
})Combining Error Recovery and Logging
Layer whenComplete for logging and exceptionally for recovery in the same pipeline — they compose cleanly.
fetchData()
.whenComplete((r, ex) -> { if (ex != null) log.error("Failed", ex); })
.exceptionally(ex -> fallback())
.thenAccept(result -> process(result));Quick Check
Which CompletableFuture method is called ONLY when the future fails?
Recap
Use exceptionally for fallback values, handle for transforming both outcomes, whenComplete for side effects. Build async HTTP pipelines with Java 11 HttpClient + thenCompose + allOf.
Frequently asked questions
Is the “Error Handling and Async HTTP Pipeline” lesson free?
Yes — the full text of “Error Handling and Async HTTP Pipeline” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.
What will I learn in “Error Handling and Async HTTP Pipeline”?
Handle errors with exceptionally, handle, and whenComplete, then build an async HTTP client pipeline. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Java Academy?
No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Error Handling and Async HTTP Pipeline” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Java Academy lesson?
Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Creating and Completing CompletableFutures
- Chaining with thenApply and thenCompose
- Combining Futures: allOf and anyOf
- Error Handling and Async HTTP Pipeline