apache/dubbo · error · RpcException

Directory of type {simpleName} already destroyed for service

Error message

Directory of type {simpleName} already destroyed for service {serviceKey} from registry {url}

What it means

Thrown by AbstractDirectory.list() when the directory's 'destroyed' flag is true. Once a Directory is destroyed (consumer unsubscribed, ReferenceConfig destroyed, or module shutting down), it refuses to list invokers and throws RpcException. The directory is the consumer-side registry of providers for a service reference.

Source

Thrown at dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java:206

            }
            this.consumerUrl = consumerUrlFrom.addParameters(queryMap);
        }

        this.connectivityExecutor = applicationModel
                .getFrameworkModel()
                .getBeanFactory()
                .getBean(FrameworkExecutorRepository.class)
                .getConnectivityScheduledExecutor();
        Configuration configuration = ConfigurationUtils.getGlobalConfiguration(url.getOrDefaultModuleModel());
        this.reconnectTaskTryCount = configuration.getInt(RECONNECT_TASK_TRY_COUNT, DEFAULT_RECONNECT_TASK_TRY_COUNT);
        this.reconnectTaskPeriod = configuration.getInt(RECONNECT_TASK_PERIOD, DEFAULT_RECONNECT_TASK_PERIOD);
        setRouterChain(routerChain);
    }

    @Override
    public List<Invoker<T>> list(Invocation invocation) throws RpcException {
        if (destroyed) {
            throw new RpcException(
                    "Directory of type " + this.getClass().getSimpleName() + " already destroyed for service "
                            + getConsumerUrl().getServiceKey() + " from registry " + getUrl());
        }

        BitList<Invoker<T>> availableInvokers;
        SingleRouterChain<T> singleChain = null;
        try {
            if (routerChain != null) {
                routerChain.getLock().readLock().lock();
            }
            boolean lockAcquired = false;
            try {
                if (!invokerRefreshReadLock.tryLock(LockUtils.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)) {
                    throw new RpcException(
                            "Failed to acquire read lock on invokerRefreshLock within timeout. " + "Timeout: "
                                    + LockUtils.DEFAULT_TIMEOUT + "ms, " + "Lock state: [readLockHeld="
                                    + invokerRefreshLock.getReadLockCount() + ", writeLockHeld="
                                    + invokerRefreshLock.isWriteLocked() + ", writeLockHeldByCurrentThread="

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Stop submitting new RPC calls before destroying the reference/container; drain in-flight calls first.
  2. Check ReferenceConfigCache / the reference's availability (or the ClusterInvoker.isDestroyed()) before invoking in late lifecycle phases.
  3. Fix shutdown ordering so application business executors are halted before the Dubbo module/reference is destroyed.

Example fix

// before: invoking after shutdown
referenceConfig.destroy();
service.foo(); // throws

// after: guard or order shutdown
if (!clusterInvoker.isDestroyed()) {
    service.foo();
}
// destroy only after all callers are quiesced
Defensive patterns

Strategy: validation

Validate before calling

// Check directory liveness before issuing an RPC
Directory<T> dir = clusterInvoker.getDirectory();
if (dir.isDestroyed()) {
    // skip / fast-fail gracefully instead of triggering the RpcException
    return null;
}
return clusterInvoker.invoke(invocation);

Try / catch

try {
    service.foo();
} catch (RpcException e) {
    if (e.getMessage().contains("already destroyed")) {
        // consumer is shutting down; do not retry, log and degrade
        log.warn("Reference destroyed; skipping call");
        return fallback();
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling a service (RpcInvocation) through a Dubbo reference whose underlying Directory has been destroyed — i.e., after ReferenceConfig.destroy(), after the Spring container closes, or after dubbo-bootstrap.stop().

Common situations: Application shutdown ordering: business threads still invoking after the Dubbo context is torn down; cached service proxies used after @DubboReference bean was destroyed; a hot-redeploy that destroys the old reference while in-flight calls continue.

Related errors


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