{"record":{"id":"c92b67fd07dd14ed","repo":"alibaba/canal","slug":"extension-type-is-not-extension-because-witho-c92b67","errorCode":null,"errorMessage":"Extension type({}) is not extension, because WITHOUT @{} Annotation!","messagePattern":"Extension type\\((.+?)\\) is not extension, because WITHOUT @(.+?) Annotation!","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"connector/core/src/main/java/com/alibaba/otter/canal/connector/core/spi/ExtensionLoader.java","lineNumber":77,"sourceCode":"\n    private ConcurrentHashMap<String, IllegalStateException>         exceptions                 = new ConcurrentHashMap<>();\n\n    private static <T> boolean withExtensionAnnotation(Class<T> type) {\n        return type.isAnnotationPresent(SPI.class);\n    }\n\n    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {\n        return getExtensionLoader(type, DEFAULT_CLASSLOADER_POLICY);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type, String classLoaderPolicy) {\n        if (type == null) throw new IllegalArgumentException(\"Extension type == null\");\n        if (!type.isInterface()) {\n            throw new IllegalArgumentException(\"Extension type(\" + type + \") is not interface!\");\n        }\n        if (!withExtensionAnnotation(type)) {\n            throw new IllegalArgumentException(\"Extension type(\" + type + \") is not extension, because WITHOUT @\"\n                                               + SPI.class.getSimpleName() + \" Annotation!\");\n        }\n\n        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);\n        if (loader == null) {\n            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type, classLoaderPolicy));\n            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);\n        }\n        return loader;\n    }\n\n    public ExtensionLoader(Class<?> type){\n        this.type = type;\n        this.classLoaderPolicy = DEFAULT_CLASSLOADER_POLICY;\n    }\n\n    public ExtensionLoader(Class<?> type, String classLoaderPolicy){\n        this.type = type;","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/alibaba/canal/blob/87be50e87686a3e8af08c368d0e1ffd1f59eb04a/connector/core/src/main/java/com/alibaba/otter/canal/connector/core/spi/ExtensionLoader.java#L59-L95","documentation":"Thrown by ExtensionLoader.getExtensionLoader when the interface type lacks the @SPI annotation. The annotation declares the default implementation name; without it the loader refuses to manage the type because it cannot know the SPI contract/default. This mirrors Dubbo's SPI requirement.","triggerScenarios":"Calling getExtensionLoader on an interface that is not annotated with com.alibaba.otter.canal.connector.core.spi.SPI.","commonSituations":"Introducing a new extension point and forgetting to annotate the interface with @SPI(\"defaultName\"); using a plain library interface by mistake; SPI annotation import collision (a different SPI annotation class).","solutions":["Annotate the interface with @SPI and a default name: @SPI(\"rocketmq\") public interface CanalMQProducer { ... }.","Confirm you imported com.alibaba.otter.canal.connector.core.spi.SPI, not a third-party @SPI.","Only load types that are true canal extension points."],"exampleFix":"// before\npublic interface CanalMQProducer { ... }\n// after\n@SPI(\"rocketmq\")\npublic interface CanalMQProducer { ... }","handlingStrategy":"validation","validationCode":"// Verify the @SPI annotation is present before loading\nif (!type.isAnnotationPresent(com.alibaba.otter.canal.connector.core.spi.SPI.class)) {\n    throw new IllegalStateException(type + \" is not annotated @SPI; cannot be an extension point\");\n}","typeGuard":"boolean isSpiInterface(Class<?> type) {\n    return type != null && type.isInterface()\n        && type.isAnnotationPresent(\n            com.alibaba.otter.canal.connector.core.spi.SPI.class);\n}","tryCatchPattern":null,"preventionTips":["Annotate every new extension interface with @SPI(\"defaultName\").","Import the canal SPI annotation, not a foreign one."],"tags":["spi","annotation","extension-loader"],"backgroundTag":null,"analyzedSha":"87be50e87686a3e8af08c368d0e1ffd1f59eb04a","analyzedAt":"2026-08-14T04:30:11.918Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}