{"record":{"id":"1c7d0cecab2c6602","repo":"apache/dubbo","slug":"exception-occurred-when-loading-extension-class-i","errorCode":null,"errorMessage":"Exception occurred when loading extension class (interface: {})","messagePattern":"Exception occurred when loading extension class \\(interface: (.+?)\\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java","lineNumber":971,"sourceCode":"    }\n\n    private Map<String, Class<?>> getExtensionClasses() {\n        Map<String, Class<?>> classes = cachedClasses.get();\n        if (classes == null) {\n            loadExtensionClassesLock.lock();\n            try {\n                classes = cachedClasses.get();\n                if (classes == null) {\n                    try {\n                        classes = loadExtensionClasses();\n                    } catch (InterruptedException e) {\n                        logger.error(\n                                COMMON_ERROR_LOAD_EXTENSION,\n                                \"\",\n                                \"\",\n                                \"Exception occurred when loading extension class (interface: \" + type + \")\",\n                                e);\n                        throw new IllegalStateException(\n                                \"Exception occurred when loading extension class (interface: \" + type + \")\", e);\n                    }\n                    cachedClasses.set(classes);\n                }\n            } finally {\n                loadExtensionClassesLock.unlock();\n            }\n        }\n        return classes;\n    }\n\n    /**\n     * synchronized in getExtensionClasses\n     */\n    @SuppressWarnings(\"deprecation\")\n    private Map<String, Class<?>> loadExtensionClasses() throws InterruptedException {\n        checkDestroyed();\n        cacheDefaultExtensionName();","sourceCodeStart":953,"sourceCodeEnd":989,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java#L953-L989","documentation":"Thrown by getExtensionClasses() when loadExtensionClasses() raises InterruptedException while scanning and parsing META-INF/dubbo/, META-INF/services/, and META-INF/dubbo/internal/ SPI resource files. The class loading of the SPI registry is interruptible, and if the thread is interrupted (interrupt status set) during the scan, this error wraps the InterruptedException and is re-thrown as IllegalStateException. It is also logged via COMMON_ERROR_LOAD_EXTENSION.","triggerScenarios":"The thread performing the first lazy load of extension classes (inside the loadExtensionClassesLock) receives Thread.interrupt() during the resource enumeration or URL stream reading of SPI config files. This typically happens in app shutdown hooks, thread-pool reclamation, or frameworks that interrupt worker threads.","commonSituations":"Application shutdown races with lazy SPI initialization; a thread pool that interrupts idle threads while Dubbo is still warming up; running inside a container/scheduler that sets interrupt status; very slow classpath/IO making the SPI scan overlap with an interrupt-driven lifecycle event.","solutions":["Ensure SPI extensions are loaded eagerly at startup (call getSupportedExtensions() during boot) before the application reaches a state where threads may be interrupted.","If the thread was legitimately interrupted, restore the interrupt status in your catch block (Thread.currentThread().interrupt()) and let the caller decide whether to retry.","Avoid calling getExtension/getAdaptiveExtension from threads that may be interrupted mid-flight (shutdown hooks, cancelled tasks)."],"exampleFix":"// before: lazy load races with shutdown interrupt\nRuntime.getRuntime().addShutdownHook(new Thread(() -> {\n    loader.getExtension(\"x\"); // may throw [144] if interrupt overlaps\n}));\n\n// after: pre-load at startup, restore interrupt in handler\nloader.getSupportedExtensions(); // warm the cache early\ntry {\n    loader.getExtension(\"x\");\n} catch (IllegalStateException e) {\n    if (Thread.interrupted()) { /* shutdown race; expected */ }\n}","handlingStrategy":"try-catch","validationCode":"// Warm the SPI cache early in the lifecycle before interrupts are possible\nextensionDirector.getExtensionLoader(MySpi.class).getSupportedExtensions();","typeGuard":null,"tryCatchPattern":"try {\n    loader.getExtension(name);\n} catch (IllegalStateException e) {\n    if (e.getCause() instanceof InterruptedException) {\n        Thread.currentThread().interrupt(); // restore status\n        log.warn(\"SPI load interrupted; will retry on next access\");\n    } else throw e;\n}","preventionTips":["Eagerly initialize SPI extensions during application bootstrap, not lazily during request handling.","Avoid calling getExtension from shutdown hooks or interruptible worker threads.","Restore interrupt status if you catch an InterruptedException-derived error."],"tags":["dubbo-spi","interrupted","concurrency","class-loading","extension-loader"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}