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&#x27;t cascade

Retry individual branches, not the whole fan-out. And only retry idempotent tools — never blindly retry actions with side effects.