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
- Make the class implement io.karatelabs.core.RunListener (or RunListenerFactory)
- Check imports — ensure the interface is Karate's, not a same-named class from another dependency
- Verify karate-core version matches what the listener was compiled against
- 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
- Implement a marker unit test: assert isKarateListener(Class.forName(name))
- Compile listeners against the same karate-core version as the runner
- Only register classes through a shared factory helper that type-checks first
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
- configure 'logging' expects a map, got
- configure 'logging.mask' expects a map, got
- Class not found
- Failed to instantiate
- Invalid callbackPort type: " + portValue.getClass()
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)