apache/dubbo · error · RpcException
Local service for {getUrl().getServiceInterface()} has not b
Error message
Local service for {getUrl().getServiceInterface()} has not been exposed yet! What it means
Thrown by ScopeClusterInvoker.isInjvmExported when the call is forced to be local (scope=local, or the injvm protocol flag set / RpcContext local-invoke requested) but the service is NOT exported in the current JVM (isExported is false). It refuses to make a remote call because the configuration demanded local scope, yet no local exporter exists.
Source
Thrown at dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/support/wrapper/ScopeClusterInvoker.java:272
*/
private boolean isInjvmExported() {
Boolean localInvoke = RpcContext.getServiceContext().getLocalInvoke();
boolean isExportedValue = isExported.get();
boolean localOnce = (localInvoke != null && localInvoke);
// Determine whether this call is local
if (isExportedValue && localOnce) {
return true;
}
// Determine whether this call is remote
if (localInvoke != null && !localInvoke) {
return false;
}
// When calling locally, determine whether it does not meet the requirements
if (!isExportedValue && (isForceLocal() || localOnce)) {
// If it's supposed to be exported to the local JVM ,but it's not, throw an exception
throw new RpcException(
"Local service for " + getUrl().getServiceInterface() + " has not been exposed yet!");
}
return isExportedValue && injvmFlag;
}
private boolean isForceLocal() {
return SCOPE_LOCAL.equalsIgnoreCase(getUrl().getParameter(SCOPE_KEY))
|| Boolean.TRUE.toString().equalsIgnoreCase(getUrl().getParameter(LOCAL_PROTOCOL));
}
/**
* Creates a new Invoker for the current ScopeClusterInvoker and exports it to the local JVM.
*/
private void createInjvmInvoker(Exporter<?> exporter) {
if (injvmInvoker == null) {
synchronized (createLock) {
if (injvmInvoker == null) {View on GitHub (pinned to 3a3043227f)
Solutions
- Expose the service locally in the same JVM (ensure the provider is configured and started, e.g. scope=local export) so an injvm exporter exists before the call.
- Remove the local-scope forcing (scope=local / injvm flag / setLocalInvoke(true)) to allow a normal remote invocation.
- Fix provider startup ordering so the injvm exporter is registered before the consumer issues a local-scoped call.
- Verify the service interface name in the message matches a service this JVM actually exposes.
Example fix
// before: consumer forces local scope but service not exported in JVM <dubbo:reference interface="com.acme.Svc" scope="local"/> // after: allow remote, or also expose locally <dubbo:reference interface="com.acme.Svc" scope="remote"/> <!-- plus, if local is desired: --> <dubbo:service interface="com.acme.Svc" ref="svcImpl" scope="local"/>
Defensive patterns
Strategy: validation
Validate before calling
// Before issuing a local-scoped call, confirm a local exporter exists
if (!isLocalExporterPresent(url.getServiceInterface())) {
throw new IllegalStateException("service not exported locally; cannot use scope=local");
} Try / catch
try {
return service.call(req);
} catch (RpcException e) {
if (e.getMessage() != null && e.getMessage().endsWith("has not been exposed yet!")) {
log.error("local scope forced but no injvm exporter; expose locally or use remote", e);
}
throw e;
} Prevention
- Expose the service in-process before forcing scope=local.
- Avoid scope=local on pure-consumer applications.
- Order provider export before consumer local-scoped calls.
When it happens
Trigger: Url config scope=local (or LOCAL_PROTOCOL/injvm flag true) and the service was never injvm-exported in this JVM; or RpcContext.getServiceContext().setLocalInvoke(true) on a consumer whose provider side has not exposed the service locally; calling a service from a different application/module context that never registered an injvm exporter.
Common situations: Enabling scope=local on a pure consumer application that does not also expose the service in-process; provider failed to start/export so no local exporter was created; cross-module calls where the exporting module loaded later (startup ordering); misreading injvm vs scope semantics and forcing local on a remote-only service.
Related errors
- The local implementation class <localClass.getName()> not im
- INTERNAL_ERROR
- Directory of type {simpleName} already destroyed for service
- Failed to acquire read lock on invokerRefreshLock within tim
- Interrupted while acquiring read lock for invoker access, ca
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/1f9fb3e2efdb4988.
Report an issue: GitHub.