alibaba/canal · error · IllegalStateException

Error when load extension class(interface: ${type}, class li

Error message

Error when load extension class(interface: ${type}, class line: ${clazz.getName()}), class ${clazz.getName()}is not subtype of interface.

What it means

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.

Source

Thrown at connector/core/src/main/java/com/alibaba/otter/canal/connector/core/spi/ExtensionLoader.java:347

                            while ((line = reader.readLine()) != null) {
                                final int ci = line.indexOf('#');
                                if (ci >= 0) line = line.substring(0, ci);
                                line = line.trim();
                                if (line.length() > 0) {
                                    try {
                                        String name = null;
                                        int i = line.indexOf('=');
                                        if (i > 0) {
                                            name = line.substring(0, i).trim();
                                            line = line.substring(i + 1).trim();
                                        }
                                        if (line.length() > 0) {
                                            Class<?> clazz = classLoader.loadClass(line);
                                            // Class<?> clazz =
                                            // Class.forName(line, true,
                                            // classLoader);
                                            if (!type.isAssignableFrom(clazz)) {
                                                throw new IllegalStateException("Error when load extension class(interface: "
                                                                                + type
                                                                                + ", class line: "
                                                                                + clazz.getName()
                                                                                + "), class "
                                                                                + clazz.getName()
                                                                                + "is not subtype of interface.");
                                            } else {
                                                try {
                                                    clazz.getConstructor(type);
                                                } catch (NoSuchMethodException e) {
                                                    clazz.getConstructor();
                                                    String[] names = NAME_SEPARATOR.split(name);
                                                    if (names != null && names.length > 0) {
                                                        for (String n : names) {
                                                            if (!cachedNames.containsKey(clazz)) {
                                                                cachedNames.put(clazz, n);
                                                            }
                                                            Class<?> c = extensionClasses.get(n);

View on GitHub (pinned to 87be50e876)

Solutions

  1. Correct the descriptor line so the class implements the extension interface (verify classpath FQN).
  2. Check load-time logs ('Exception when load extension class') to identify which line/class was rejected.
  3. Rebuild/redeploy the plugin jar with the correct implementation-extends-interface relationship.

Example fix

# before (META-INF/canal/...CanalMQProducer)
rocketmq=com.alibaba...SomeUnrelatedClass
# after
rocketmq=com.alibaba.otter.canal.connector.rocketmq.producer.CanalRocketMQProducer
Defensive patterns

Strategy: validation

Validate before calling

// At build/test time, assert every SPI descriptor line maps to a subtype
Class<?> ext = Class.forName(implFqn);
if (!interfaceType.isAssignableFrom(ext)) {
    throw new IllegalStateException(ext + " does not implement " + interfaceType);
}

Type guard

boolean isSubtypeOf(Class<?> impl, Class<?> iface) {
    return impl != null && iface != null && iface.isAssignableFrom(impl);
}

Try / catch

// NOTE: thrown-and-stored at load time; surfaces later as 'could not be found'.
// Catch that downstream symptom and inspect logs for the subtype error.
try {
    loader.getExtension(name, spiDir, standbyDir);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("could not be found")) {
        // grep logs: 'is not subtype of interface' -> fix descriptor line
    }
    throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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