karatelabs/karate · error · RuntimeException

boot.ext(' '): does not implement io.karatelabs.core.Ext

Error message

boot.ext('${name}'): ${className} does not implement io.karatelabs.core.Ext

What it means

boot.ext(name) resolves the name to a class via the naming convention, instantiates it, and verifies the instance implements io.karatelabs.core.Ext. If the class loads but does not implement Ext, a RuntimeException is thrown naming both the requested extension and the offending class.

Solutions

  1. Make the resolved class implement io.karatelabs.core.Ext (implement its methods).
  2. Rebuild the extension against the karate version in use so it implements the current Ext interface/package.
  3. Remove or rename the unrelated class occupying the name-convention FQCN.
  4. Use a different extension name whose conventional class is free.

Example fix

// before
public class MyExt { } // wrong package, no Ext interface
// after
import io.karatelabs.core.Ext;
public class MyExt implements Ext {
    public void onBoot(Suite suite) { }
}
Defensive patterns

Strategy: validation

Validate before calling

// before boot.ext(name), verify the class implements Ext
Class<?> c = Class.forName(resolvedFqcn);
if (!io.karatelabs.core.Ext.class.isAssignableFrom(c)) {
    throw new IllegalStateException(resolvedFqcn + " must implement io.karatelabs.core.Ext");
}

Try / catch

try {
    boot.ext(name);
} catch (RuntimeException e) {
    // message names the offending class — fix its implements clause or rebuild against current karate
}

Prevention

When it happens

Trigger: A class exists at the name-convention location (so ClassNotFoundException does not fire) but it does not implement io.karatelabs.core.Ext — e.g. an unrelated class occupying the expected FQCN, or an Ext implementation compiled against a different package/version of the interface.

Common situations: Upgrading karate where the Ext interface package changed (e.g. io.karatelabs vs older package) leaving an old implementation in place; accidentally placing a helper class at the conventional extension FQCN; two extensions colliding on the same class name.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/1b83757c9c301c7c. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/core/BootBinding.java:227

     * <p>Same name twice returns the same singleton instance.</p>
     */
    public Ext ext(String name) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("boot.ext: name is null or empty");
        }
        // Singleton-per-name within this BootBinding (which is itself per-Suite).
        for (Ext existing : exts) {
            if (name.equals(extShortName(existing))) {
                return existing;
            }
        }
        String className = extClassName(name);
        Ext ext;
        try {
            Class<?> cls = Class.forName(className);
            Object instance = cls.getDeclaredConstructor().newInstance();
            if (!(instance instanceof Ext)) {
                throw new RuntimeException(
                        "boot.ext('" + name + "'): " + className
                                + " does not implement io.karatelabs.core.Ext");
            }
            ext = (Ext) instance;
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(
                    "boot.ext('" + name + "'): not on classpath. Expected "
                            + className + " (name-convention resolution).", e);
        } catch (Exception e) {
            throw new RuntimeException(
                    "boot.ext('" + name + "'): failed to construct " + className
                            + " — " + e.getMessage(), e);
        }
        // Fire onBoot eagerly per K43. Throws here fail the Suite.
        ext.onBoot(suite);
        exts.add(ext);
        registrar.accept(ext);
        logger.info("ext booted: {} ({})", name, ext.getClass().getName());

View on GitHub (pinned to a22eb90246)