karatelabs/karate · error · IllegalArgumentException

Class must implement RunListenerFactory or RunListener

Error message

Class <className> must implement RunListenerFactory or RunListener

What it means

Runner.listenerFactory instantiates each configured listener class by name and requires it to implement either RunListenerFactory (creates listeners per run) or RunListener (used directly). A class implementing neither is rejected with an IllegalArgumentException.

Solutions

  1. Make the class implement io.karatelabs.core.RunListener (or RunListenerFactory)
  2. Check imports — ensure the interface is Karate's, not a same-named class from another dependency
  3. Verify karate-core version matches what the listener was compiled against
  4. If the class holds per-run state, implement RunListenerFactory instead of RunListener

Example fix

// before
public class MyListener { }
// after
import io.karatelabs.core.RunListener;
public class MyListener implements RunListener { /* callbacks */ }
Defensive patterns

Strategy: type-guard

Validate before calling

Class<?> c = Class.forName(name);
if (!RunListener.class.isAssignableFrom(c) && !RunListenerFactory.class.isAssignableFrom(c))
    throw new IllegalArgumentException(name + " must implement RunListener or RunListenerFactory");

Type guard

boolean isKarateListener(Class<?> c) { return RunListener.class.isAssignableFrom(c) || RunListenerFactory.class.isAssignableFrom(c); }

Try / catch

try { builder.listener(name); } catch (IllegalArgumentException e) { if (e.getMessage().endsWith("must implement RunListenerFactory or RunListener")) { log.error("{}: add 'implements RunListener'", name); } throw e; }

Prevention

When it happens

Trigger: Runner.builder().listener("com.example.MyClass") where MyClass implements no Karate listener interface, or an instance passed to the instance-accepting overload whose type is neither interface.

Common situations: Typo/import mistake where the class implements a similarly-named interface from another library; custom listener compiled against a different Karate version whose interface package changed; forgetting to implement RunListener after removing the extends clause.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/core/Runner.java:681

        }

        /**
         * Add a run listener factory by class name (supports no-arg constructor).
         * Used for CLI --listener-factory option.
         * @param className fully qualified class name
         */
        public Builder listenerFactory(String className) {
            if (className != null && !className.isEmpty()) {
                try {
                    Class<?> clazz = Class.forName(className);
                    Object instance = clazz.getDeclaredConstructor().newInstance();
                    if (instance instanceof RunListenerFactory factory) {
                        listenerFactories.add(factory);
                    } else if (instance instanceof RunListener listener) {
                        // If it's a RunListener, wrap it in a factory that returns the same instance
                        listeners.add(listener);
                    } else {
                        throw new IllegalArgumentException(
                                "Class " + className + " must implement RunListenerFactory or RunListener");
                    }
                } catch (ClassNotFoundException e) {
                    throw new IllegalArgumentException("Class not found: " + className, e);
                } catch (Exception e) {
                    throw new IllegalArgumentException("Failed to instantiate " + className, e);
                }
            }
            return this;
        }

        /**
         * Add a result listener for streaming test results.
         */
        public Builder resultListener(ResultListener listener) {
            if (listener != null) {
                resultListeners.add(listener);
            }

View on GitHub (pinned to a22eb90246)