{"record":{"id":"5dfd63f018df54c5","repo":"apache/dubbo","slug":"directory-of-type-simplename-already-destroyed-f","errorCode":null,"errorMessage":"Directory of type {simpleName} already destroyed for service {serviceKey} from registry {url}","messagePattern":"Directory of type (.+?) already destroyed for service (.+?) from registry (.+?)","errorType":"exception","errorClass":"RpcException","httpStatus":null,"severity":"error","filePath":"dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java","lineNumber":206,"sourceCode":"            }\n            this.consumerUrl = consumerUrlFrom.addParameters(queryMap);\n        }\n\n        this.connectivityExecutor = applicationModel\n                .getFrameworkModel()\n                .getBeanFactory()\n                .getBean(FrameworkExecutorRepository.class)\n                .getConnectivityScheduledExecutor();\n        Configuration configuration = ConfigurationUtils.getGlobalConfiguration(url.getOrDefaultModuleModel());\n        this.reconnectTaskTryCount = configuration.getInt(RECONNECT_TASK_TRY_COUNT, DEFAULT_RECONNECT_TASK_TRY_COUNT);\n        this.reconnectTaskPeriod = configuration.getInt(RECONNECT_TASK_PERIOD, DEFAULT_RECONNECT_TASK_PERIOD);\n        setRouterChain(routerChain);\n    }\n\n    @Override\n    public List<Invoker<T>> list(Invocation invocation) throws RpcException {\n        if (destroyed) {\n            throw new RpcException(\n                    \"Directory of type \" + this.getClass().getSimpleName() + \" already destroyed for service \"\n                            + getConsumerUrl().getServiceKey() + \" from registry \" + getUrl());\n        }\n\n        BitList<Invoker<T>> availableInvokers;\n        SingleRouterChain<T> singleChain = null;\n        try {\n            if (routerChain != null) {\n                routerChain.getLock().readLock().lock();\n            }\n            boolean lockAcquired = false;\n            try {\n                if (!invokerRefreshReadLock.tryLock(LockUtils.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)) {\n                    throw new RpcException(\n                            \"Failed to acquire read lock on invokerRefreshLock within timeout. \" + \"Timeout: \"\n                                    + LockUtils.DEFAULT_TIMEOUT + \"ms, \" + \"Lock state: [readLockHeld=\"\n                                    + invokerRefreshLock.getReadLockCount() + \", writeLockHeld=\"\n                                    + invokerRefreshLock.isWriteLocked() + \", writeLockHeldByCurrentThread=\"","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-cluster/src/main/java/org/apache/dubbo/rpc/cluster/directory/AbstractDirectory.java#L188-L224","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","solutions":["Stop submitting new RPC calls before destroying the reference/container; drain in-flight calls first.","Check ReferenceConfigCache / the reference's availability (or the ClusterInvoker.isDestroyed()) before invoking in late lifecycle phases.","Fix shutdown ordering so application business executors are halted before the Dubbo module/reference is destroyed."],"exampleFix":"// before: invoking after shutdown\nreferenceConfig.destroy();\nservice.foo(); // throws\n\n// after: guard or order shutdown\nif (!clusterInvoker.isDestroyed()) {\n    service.foo();\n}\n// destroy only after all callers are quiesced","handlingStrategy":"validation","validationCode":"// Check directory liveness before issuing an RPC\nDirectory<T> dir = clusterInvoker.getDirectory();\nif (dir.isDestroyed()) {\n    // skip / fast-fail gracefully instead of triggering the RpcException\n    return null;\n}\nreturn clusterInvoker.invoke(invocation);","typeGuard":null,"tryCatchPattern":"try {\n    service.foo();\n} catch (RpcException e) {\n    if (e.getMessage().contains(\"already destroyed\")) {\n        // consumer is shutting down; do not retry, log and degrade\n        log.warn(\"Reference destroyed; skipping call\");\n        return fallback();\n    }\n    throw e;\n}","preventionTips":["Drain in-flight RPCs before destroying references / shutting down.","Check Directory.isDestroyed() (or ClusterInvoker.isDestroyed()) in late lifecycle code.","Order application shutdown so business executors stop before Dubbo tears down."],"tags":["cluster","directory","lifecycle","consumer"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}