alibaba/canal · error · IllegalStateException
more than 1 default extension name on extension ${type.getNa
Error message
more than 1 default extension name on extension ${type.getName()}: ${Arrays.toString(names)} What it means
Thrown by loadExtensionClasses when the @SPI annotation value on the interface contains more than one comma-separated name after split. The SPI default-value contract allows exactly one default extension name; multiple defaults are ambiguous and rejected before any class is loaded.
Source
Thrown at connector/core/src/main/java/com/alibaba/otter/canal/connector/core/spi/ExtensionLoader.java:257
// the app using Spring Tools Suit play
// button.
jarPath = jarPath.replaceAll("/classes/.*", "/classes/");
}
Path path = Paths.get(jarPath).getParent(); // Paths - from java 8
if (path != null) {
return path.toString();
}
return null;
}
private Map<String, Class<?>> loadExtensionClasses(String spiDir, String standbyDir) {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<>();
if (spiDir != null && standbyDir != null) {
// 1. plugin folder,customized extension classLoader
// (jar_dir/plugin)
String dir = File.separator + this.getJarDirectoryPath() + spiDir; // +
// "plugin";
File externalLibDir = new File(dir);
if (!externalLibDir.exists()) {
externalLibDir = new File(File.separator + this.getJarDirectoryPath() + standbyDir);
}View on GitHub (pinned to 87be50e876)
Solutions
- Set exactly one default name in the annotation: @SPI("rocketmq").
- If multiple implementations exist, register each as a named entry in the SPI descriptor and select at runtime via the name, not via multiple defaults.
Example fix
// before
@SPI("rocketmq,kafka")
public interface CanalMQProducer { ... }
// after
@SPI("rocketmq")
public interface CanalMQProducer { ... } Defensive patterns
Strategy: validation
Validate before calling
// Validate the @SPI default value at build/test time
import com.alibaba.otter.canal.connector.core.spi.SPI;
SPI a = MyInterface.class.getAnnotation(SPI.class);
if (a != null && a.value().trim().length() > 0) {
String[] names = a.value().trim().split("\\s*,+\\s*");
if (names.length > 1) {
throw new AssertionError("@SPI on " + MyInterface.class
+ " declares >1 default: " + java.util.Arrays.toString(names));
}
} Prevention
- Keep exactly one name in @SPI values.
- Add a unit test asserting each @SPI interface has at most one default name.
When it happens
Trigger: Declaring @SPI("a,b") or @SPI("a, b, c") on an extension interface — the NAME_SEPARATOR split yields >1 element.
Common situations: Developer lists several 'preferred' defaults on the annotation thinking the loader will pick one; copy-paste from a multi-impl comment; accidental comma in the annotation value.
Related errors
- Extension type({}) is not extension, because WITHOUT @{} Ann
- Extension name == null
- Extension instance(name: {}, class: {}) could not be instan
- Extension instance(name: ${name}, class: ${type}) could not
- Extension type == null
AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14).
Data as JSON: /api/errors/0cd67999e8d12dac.
Report an issue: GitHub.