Never let one branch kill the fan-out
Wrap every branch in .exceptionally() to convert failures into AgentResponse.error() values. The joiner sees a full list — some successful, some errors.
CompletableFuture safe = branch
.orTimeout(15, TimeUnit.SECONDS)
.exceptionally(e -> AgentResponse.error(agentName, e)); Advertisement
Partial-result thresholds
Sometimes you can tolerate M of N failures. Codify it — if fewer than K branches succeeded, return an overall failure.
long ok = results.stream().filter(r -> !r.isError()).count();
if (ok < MIN_SUCCESSES) {
throw new PartialFailureException(results);
}Advertisement
Retries that don't cascade
Retry individual branches, not the whole fan-out. And only retry idempotent tools — never blindly retry actions with side effects.