alibaba/canal · error · IllegalStateException

Extension instance(name: ${name}, class: ${type}) could not

Error message

Extension instance(name: ${name}, class: ${type})  could not be instantiated: ${t.getMessage()}

What it means

Thrown by the private 4-arg createExtension(name, key,...) when the keyed instance cannot be instantiated — clazz.newInstance() threw. Same as 210 but on the per-key path (instance stored under EXTENSION_KEY_INSTANCE keyed by name+key).

Source

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

        }
    }

    @SuppressWarnings("unchecked")
    private T createExtension(String name, String key, String spiDir, String standbyDir) {
        Class<?> clazz = getExtensionClasses(spiDir, standbyDir).get(name);
        if (clazz == null) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type
                                            + ")  could not be instantiated: class could not be found");
        }
        try {
            T instance = (T) EXTENSION_KEY_INSTANCE.get(name + "-" + key);
            if (instance == null) {
                EXTENSION_KEY_INSTANCE.putIfAbsent(name + "-" + key, clazz.newInstance());
                instance = (T) EXTENSION_KEY_INSTANCE.get(name + "-" + key);
            }
            return instance;
        } catch (Throwable t) {
            throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type
                                            + ")  could not be instantiated: " + t.getMessage(), t);
        }
    }

    private Map<String, Class<?>> getExtensionClasses(String spiDir, String standbyDir) {
        Map<String, Class<?>> classes = cachedClasses.get();
        if (classes == null) {
            synchronized (cachedClasses) {
                classes = cachedClasses.get();
                if (classes == null) {
                    classes = loadExtensionClasses(spiDir, standbyDir);
                    cachedClasses.set(classes);
                }
            }
        }

        return classes;
    }

View on GitHub (pinned to 87be50e876)

Solutions

  1. Add a public no-arg constructor to the implementation.
  2. Move per-key init out of the constructor into an init(key) method invoked after instantiation.
  3. Read the wrapped cause to identify the underlying instantiation error and fix it.

Example fix

// before
public MyProducer(String key) { this.key = key; }
// after
public MyProducer() { }
public void init(String key) { this.key = key; }
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate keyed impl instantiability
Class<?> impl = resolveImplClass(interfaceType, name);
try { if (impl != null) impl.getConstructor(); }
catch (NoSuchMethodException e) {
    throw new IllegalStateException(impl + " needs a public no-arg constructor for keyed SPI");
}

Type guard

boolean isInstantiableSpi(Class<?> impl) {
    try {
        return impl != null && !Modifier.isAbstract(impl.getModifiers())
            && impl.getConstructor() != null;
    } catch (NoSuchMethodException e) { return false; }
}

Try / catch

try {
    return loader.getExtension(name, key, spiDir, standbyDir);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("could not be instantiated")) {
        Throwable c = e.getCause();
        // ensure public no-arg ctor; move per-key init out of constructor
    }
    throw e;
}

Prevention

When it happens

Trigger: The implementation class for a keyed extension has no public no-arg constructor or its constructor threw; abstract class; access restriction.

Common situations: Custom per-key extension whose constructor fails; constructor depends on per-key config not yet bound; obfuscation/security constraints.

Related errors


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