alibaba/canal · error · IllegalArgumentException
Extension type({}) is not extension, because WITHOUT @{} Ann
Error message
Extension type({}) is not extension, because WITHOUT @{} Annotation! What it means
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.
Source
Thrown at connector/core/src/main/java/com/alibaba/otter/canal/connector/core/spi/ExtensionLoader.java:77
private ConcurrentHashMap<String, IllegalStateException> exceptions = new ConcurrentHashMap<>();
private static <T> boolean withExtensionAnnotation(Class<T> type) {
return type.isAnnotationPresent(SPI.class);
}
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
return getExtensionLoader(type, DEFAULT_CLASSLOADER_POLICY);
}
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type, String classLoaderPolicy) {
if (type == null) throw new IllegalArgumentException("Extension type == null");
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
}
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @"
+ SPI.class.getSimpleName() + " Annotation!");
}
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type, classLoaderPolicy));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
public ExtensionLoader(Class<?> type){
this.type = type;
this.classLoaderPolicy = DEFAULT_CLASSLOADER_POLICY;
}
public ExtensionLoader(Class<?> type, String classLoaderPolicy){
this.type = type;View on GitHub (pinned to 87be50e876)
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.
Example fix
// before
public interface CanalMQProducer { ... }
// after
@SPI("rocketmq")
public interface CanalMQProducer { ... } Defensive patterns
Strategy: validation
Validate before calling
// Verify the @SPI annotation is present before loading
if (!type.isAnnotationPresent(com.alibaba.otter.canal.connector.core.spi.SPI.class)) {
throw new IllegalStateException(type + " is not annotated @SPI; cannot be an extension point");
} Type guard
boolean isSpiInterface(Class<?> type) {
return type != null && type.isInterface()
&& type.isAnnotationPresent(
com.alibaba.otter.canal.connector.core.spi.SPI.class);
} Prevention
- Annotate every new extension interface with @SPI("defaultName").
- Import the canal SPI annotation, not a foreign one.
When it happens
Trigger: Calling getExtensionLoader on an interface that is not annotated with com.alibaba.otter.canal.connector.core.spi.SPI.
Common situations: 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).
Related errors
- more than 1 default extension name on extension ${type.getNa
- Extension type == null
- Extension type({}) is not interface!
- Extension name == null
- Extension instance(name: {}, class: {}) could not be instan
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/c92b67fd07dd14ed.
Report an issue: GitHub.