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

  1. Set exactly one default name in the annotation: @SPI("rocketmq").
  2. 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

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


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/0cd67999e8d12dac. Report an issue: GitHub.