{"record":{"id":"de7623148803aa65","repo":"apache/dubbo","slug":"error-occurred-when-loading-extension-class-inter","errorCode":null,"errorMessage":"Error occurred when loading extension class (interface: {}, class line: {}), class {} is not subtype of interface.","messagePattern":"Error occurred when loading extension class \\(interface: (.+?), class line: (.+?)\\), class (.+?) is not subtype of interface\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java","lineNumber":1274,"sourceCode":"            for (String excludePackage : onlyExtensionClassLoaderPackages) {\n                if (className.startsWith(excludePackage + \".\")) {\n                    // if target classLoader is not ExtensionLoader's classLoader should be excluded\n                    return !Objects.equals(ExtensionLoader.class.getClassLoader(), classLoader);\n                }\n            }\n        }\n        return false;\n    }\n\n    private void loadClass(\n            ClassLoader classLoader,\n            Map<String, Class<?>> extensionClasses,\n            java.net.URL resourceURL,\n            Class<?> clazz,\n            String name,\n            boolean overridden) {\n        if (!type.isAssignableFrom(clazz)) {\n            throw new IllegalStateException(\n                    \"Error occurred when loading extension class (interface: \" + type + \", class line: \"\n                            + clazz.getName() + \"), class \" + clazz.getName() + \" is not subtype of interface.\");\n        }\n\n        boolean isActive = loadClassIfActive(classLoader, clazz);\n\n        if (!isActive) {\n            return;\n        }\n\n        if (clazz.isAnnotationPresent(Adaptive.class)) {\n            cacheAdaptiveClass(clazz, overridden);\n        } else if (isWrapperClass(clazz)) {\n            cacheWrapperClass(clazz);\n        } else {\n            if (StringUtils.isEmpty(name)) {\n                name = findAnnotationName(clazz);\n                if (name.length() == 0) {","sourceCodeStart":1256,"sourceCodeEnd":1292,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java#L1256-L1292","documentation":"Thrown by loadClass() when a class declared in a META-INF SPI configuration file does not implement/extend the SPI interface type that owns the ExtensionLoader. The check type.isAssignableFrom(clazz) fails, meaning the configured implementation class is incompatible with the contract. The message includes the interface, the offending class line name, and the mismatch.","triggerScenarios":"A META-INF/dubbo/org.apache.dubbo.rpc.Protocol file (or META-INF/services/ equivalent) contains a line 'someName=com.example.WrongClass' where com.example.WrongClass does not implement org.apache.dubbo.rpc.Protocol. loadClass() is invoked during SPI directory scanning and validates the assignability immediately.","commonSituations":"Typo in the implementation class FQN in the SPI config file; refactoring moved a class to a different interface but the config was not updated; a fat-jar/shade relocation rewrote class names but left old SPI config files on the classpath; two jars provide conflicting SPI configs where one references a class from an older incompatible version.","solutions":["Open the META-INF file named after the SPI interface (e.g. META-INF/dubbo/org.apache.dubbo.rpc.Protocol) and verify the implementation class FQN is correct and implements the interface.","If the class is correct, ensure the SPI interface in the config filename exactly matches the @SPI-annotated interface you expect.","Run 'jar tf your.jar | grep META-INF/dubbo' to find which artifact ships the offending config and fix or exclude it.","After a rename/refactor, update all SPI config files to the new class name."],"exampleFix":"# before: META-INF/dubbo/org.apache.dubbo.rpc.Protocol\nmyproto=com.example.OldProtocolName\n# throws [146] if OldProtocolName was renamed or no longer implements Protocol\n\n# after\nmyproto=com.example.NewProtocolName","handlingStrategy":"validation","validationCode":"// At build/test time, verify every SPI config entry implements the interface\nClass<?> iface = MySpi.class;\nClass<?> impl = Class.forName(configImplFqn);\nif (!iface.isAssignableFrom(impl)) {\n    throw new IllegalStateException(impl + \" does not implement \" + iface);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate SPI config files in a build-time test that loads each implementation and checks isAssignableFrom.","After renaming a class, update all META-INF SPI config files consistently.","Use 'jar tf' to audit which artifacts ship SPI configs when debugging classpath conflicts."],"tags":["dubbo-spi","spi-config","class-mismatch","classpath","extension-loader"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}