apache/dubbo · error · RpcException

Failed to forking invoke provider {selected}, but no luck to

Error message

Failed to forking invoke provider {selected}, but no luck to perform the invocation. Last error is: {e.getMessage()}

What it means

Thrown by ForkingClusterInvoker.doInvoke() when all forked (parallel) invocations fail — the bounded queue receives a Throwable once the failure count reaches the number of selected providers — or when the result poll is interrupted. The forking strategy invokes N providers in parallel and returns the first success; if none succeed, the last error is surfaced.

Source

Thrown at dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/ForkingClusterInvoker.java:120

                                executor)
                        .whenComplete((v, t) -> {
                            if (t == null) {
                                ref.offer(v);
                            } else {
                                int value = count.incrementAndGet();
                                if (value >= selected.size()) {
                                    ref.offer(t);
                                }
                            }
                        });
            });
            try {
                Object ret = ref.poll(timeout, TimeUnit.MILLISECONDS);
                if (ret instanceof Throwable) {
                    Throwable e = ret instanceof CompletionException
                            ? ((CompletionException) ret).getCause()
                            : (Throwable) ret;
                    throw new RpcException(
                            e instanceof RpcException ? ((RpcException) e).getCode() : RpcException.UNKNOWN_EXCEPTION,
                            "Failed to forking invoke provider " + selected
                                    + ", but no luck to perform the invocation. " + "Last error is: " + e.getMessage(),
                            e.getCause() != null ? e.getCause() : e);
                }
                return (Result) ret;
            } catch (InterruptedException e) {
                throw new RpcException(
                        "Failed to forking invoke provider " + selected + ", "
                                + "but no luck to perform the invocation. Last error is: " + e.getMessage(),
                        e);
            }
        } finally {
            // clear attachments which is binding to current thread.
            RpcContext.getClientAttachment().clearAttachments();
        }
    }
}

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Inspect the unwrapped 'Last error' cause — typically all forked providers failed; fix the underlying provider/network issue.
  2. Tune 'forks' (number of parallel invocations) and 'timeout' so enough healthy providers are tried within the window.
  3. If interrupted (InterruptedException branch), ensure the caller is not being cancelled mid-RPC and align timeouts with the forking window.

Example fix

# before: forks and timeout defaults cause all forks to fail / interrupt
@DubboReference(cluster = "forking")
private QueryService queryService;

# after: tune forks + timeout for the parallel fan-out
@DubboReference(cluster = "forking", forks = 3, timeout = 2000)
private QueryService queryService;
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight for forking cluster: ensure enough providers and a sane timeout
int forks = url.getParameter(FORKS_KEY, DEFAULT_FORKS);
if (forks <= 0 || directory.getAllInvokers().size() < 1) {
    return fallback();
}
// confirm timeout is long enough for the parallel fan-out window

Try / catch

try {
    return forkingService.query(q);
} catch (RpcException e) {
    if (e.getMessage().contains("Failed to forking invoke")) {
        // all forks failed or the poll was interrupted; degrade or alert
        log.error("Forking invoke failed: " + e.getMessage());
        return fallback();
    }
    throw e;
}

Prevention

When it happens

Trigger: Using cluster='forking' with 'forks' parallel providers; every forked invocation throws, so count reaches selected.size() and a Throwable is offered to the queue, which is then unwrapped and rethrown. Also thrown if the poll(timeout) is interrupted (InterruptedException).

Common situations: All forked providers fail within the fork window; the configured 'forks' exceeds available healthy providers; timeout shorter than the slowest fork so the queue reports failure; the calling thread is interrupted during the poll.

Related errors


AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14). Data as JSON: /api/errors/7517559768598395. Report an issue: GitHub.