{"record":{"id":"3d5f189c9740054b","repo":"apache/dubbo","slug":"extensiondirector-is-destroyed","errorCode":null,"errorMessage":"ExtensionDirector is destroyed","messagePattern":"ExtensionDirector is destroyed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionDirector.java","lineNumber":156,"sourceCode":"        return parent;\n    }\n\n    public void removeAllCachedLoader() {}\n\n    public void destroy() {\n        if (destroyed.compareAndSet(false, true)) {\n            for (ExtensionLoader<?> extensionLoader : extensionLoadersMap.values()) {\n                extensionLoader.destroy();\n            }\n            extensionLoadersMap.clear();\n            extensionScopeMap.clear();\n            extensionPostProcessors.clear();\n        }\n    }\n\n    private void checkDestroyed() {\n        if (destroyed.get()) {\n            throw new IllegalStateException(\"ExtensionDirector is destroyed\");\n        }\n    }\n}\n","sourceCodeStart":138,"sourceCodeEnd":160,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionDirector.java#L138-L160","documentation":"Thrown by ExtensionDirector.checkDestroyed() when any method is called on an ExtensionDirector whose destroy() method has already been invoked. ExtensionDirector is tied to a ScopeModel (application, module) and when that scope is shut down, the director and all its child ExtensionLoaders are destroyed. Any subsequent access is illegal.","triggerScenarios":"Application shutdown sequence destroys the ScopeModel and its ExtensionDirector, but a background thread, timer, or lazy-init callback subsequently tries to resolve or access an extension. Also triggered by manual calls to director.destroy() followed by continued usage. Common in hot-reload, redeploy, or graceful-shutdown scenarios.","commonSituations":"Graceful shutdown where a lingering RPC callback or scheduled task triggers extension lookup after shutdown. Integration tests that destroy the application context but continue assertions. A custom lifecycle hook calls destroy() too early. Framework restart in a container (e.g., Tomcat undeploy/redeploy) leaves stale references.","solutions":["Inspect the stack trace: identify the caller that triggers extension access after shutdown.","Ensure background threads and scheduled tasks are stopped before the ScopeModel/ExtensionDirector is destroyed.","If using DubboBootstrap, ensure all consumers/providers are fully deregistered before application exit.","Guard the call site with a check against the scope model's lifecycle state, or use a shutdown latch."],"exampleFix":"// before — background task accesses extensions after shutdown\nscheduledExecutor.scheduleAtFixedRate(() -> {\n    director.getExtensionLoader(MySpi.class).getExtension(\"impl\");\n}, 0, 1, TimeUnit.SECONDS);\n\n// after — stop the task before shutdown\nScheduledFuture<?> future = scheduledExecutor.scheduleAtFixedRate(...);\n// during shutdown:\nfuture.cancel(false);\nscheduledExecutor.shutdown();\ndubboBootstrap.stop();","handlingStrategy":"try-catch","validationCode":"// ExtensionDirector has no public isDestroyed() method.\n// Track lifecycle externally or wrap access in a try-catch.\n// If you control the ScopeModel, gate access with your own shutdown flag.\nif (!applicationShuttingDown.get()) {\n    ExtensionLoader<T> loader = director.getExtensionLoader(type);\n}","typeGuard":null,"tryCatchPattern":"try {\n    ExtensionLoader<T> loader = director.getExtensionLoader(type);\n} catch (IllegalStateException e) {\n    if (e.getMessage().equals(\"ExtensionDirector is destroyed\")) {\n        log.warn(\"ExtensionDirector already destroyed — skipping extension access\", e);\n        return null; // or fallback\n    }\n    throw e;\n}","preventionTips":["Stop all background threads and scheduled tasks before calling dubboBootstrap.stop() or ScopeModel.destroy().","Use a shutdown latch or flag to gate extension access in async callbacks.","In integration tests, destroy the application context in @AfterAll, not @AfterEach, and avoid extension access in cleanup.","Never cache ExtensionDirector references in static fields across application restarts."],"tags":["dubbo-spi","lifecycle","shutdown","concurrency","resource-management"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}