{"record":{"id":"b7921d048ad7cff1","repo":"alibaba/canal","slug":"error-when-load-extension-class-interface-type","errorCode":null,"errorMessage":"Error when load extension class(interface: ${type}, class line: ${clazz.getName()}), class ${clazz.getName()}is not subtype of interface.","messagePattern":"Error when load extension class\\(interface: (.+?), class line: (.+?)\\), class (.+?)is not subtype of interface\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"connector/core/src/main/java/com/alibaba/otter/canal/connector/core/spi/ExtensionLoader.java","lineNumber":347,"sourceCode":"                            while ((line = reader.readLine()) != null) {\n                                final int ci = line.indexOf('#');\n                                if (ci >= 0) line = line.substring(0, ci);\n                                line = line.trim();\n                                if (line.length() > 0) {\n                                    try {\n                                        String name = null;\n                                        int i = line.indexOf('=');\n                                        if (i > 0) {\n                                            name = line.substring(0, i).trim();\n                                            line = line.substring(i + 1).trim();\n                                        }\n                                        if (line.length() > 0) {\n                                            Class<?> clazz = classLoader.loadClass(line);\n                                            // Class<?> clazz =\n                                            // Class.forName(line, true,\n                                            // classLoader);\n                                            if (!type.isAssignableFrom(clazz)) {\n                                                throw new IllegalStateException(\"Error when load extension class(interface: \"\n                                                                                + type\n                                                                                + \", class line: \"\n                                                                                + clazz.getName()\n                                                                                + \"), class \"\n                                                                                + clazz.getName()\n                                                                                + \"is not subtype of interface.\");\n                                            } else {\n                                                try {\n                                                    clazz.getConstructor(type);\n                                                } catch (NoSuchMethodException e) {\n                                                    clazz.getConstructor();\n                                                    String[] names = NAME_SEPARATOR.split(name);\n                                                    if (names != null && names.length > 0) {\n                                                        for (String n : names) {\n                                                            if (!cachedNames.containsKey(clazz)) {\n                                                                cachedNames.put(clazz, n);\n                                                            }\n                                                            Class<?> c = extensionClasses.get(n);","sourceCodeStart":329,"sourceCodeEnd":365,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/connector/core/src/main/java/com/alibaba/otter/canal/connector/core/spi/ExtensionLoader.java#L329-L365","documentation":"Constructed in loadFile when an SPI descriptor line names a class that does NOT implement/extend the extension interface (type.isAssignableFrom(clazz) is false). IMPORTANT: this exception is created but then caught by the inner catch(Throwable) at ExtensionLoader.java:381 and stored into the 'exceptions' map rather than propagated; the offending class is skipped. The user therefore usually sees a later 'could not be found' (209/211) or a load-time log line, not this message directly.","triggerScenarios":"A META-INF/canal/<interface> descriptor maps a name to a class that is not a subtype of the extension interface (wrong class, stale FQN after a rename, copy-paste from another interface's descriptor).","commonSituations":"Renaming/repackaging an implementation without updating its SPI descriptor line; descriptor line referencing a class that implements a sibling interface; vendor jar shipping an incompatible implementation version.","solutions":["Correct the descriptor line so the class implements the extension interface (verify classpath FQN).","Check load-time logs ('Exception when load extension class') to identify which line/class was rejected.","Rebuild/redeploy the plugin jar with the correct implementation-extends-interface relationship."],"exampleFix":"# before (META-INF/canal/...CanalMQProducer)\nrocketmq=com.alibaba...SomeUnrelatedClass\n# after\nrocketmq=com.alibaba.otter.canal.connector.rocketmq.producer.CanalRocketMQProducer","handlingStrategy":"validation","validationCode":"// At build/test time, assert every SPI descriptor line maps to a subtype\nClass<?> ext = Class.forName(implFqn);\nif (!interfaceType.isAssignableFrom(ext)) {\n    throw new IllegalStateException(ext + \" does not implement \" + interfaceType);\n}","typeGuard":"boolean isSubtypeOf(Class<?> impl, Class<?> iface) {\n    return impl != null && iface != null && iface.isAssignableFrom(impl);\n}","tryCatchPattern":"// NOTE: thrown-and-stored at load time; surfaces later as 'could not be found'.\n// Catch that downstream symptom and inspect logs for the subtype error.\ntry {\n    loader.getExtension(name, spiDir, standbyDir);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"could not be found\")) {\n        // grep logs: 'is not subtype of interface' -> fix descriptor line\n    }\n    throw e;\n}","preventionTips":["Keep SPI descriptor FQNs in sync with implementation renames/repackages.","Add a test that loads each descriptor and asserts type.isAssignableFrom(impl)."],"tags":["spi","classpath","type-mismatch","extension-loader"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}