{"record":{"id":"d5ac8ca33fbf57c9","repo":"karatelabs/karate","slug":"class-classname-must-implement-runlistenerfactory-or","errorCode":null,"errorMessage":"Class <className> must implement RunListenerFactory or RunListener","messagePattern":"Class <className> must implement RunListenerFactory or RunListener","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"karate-core/src/main/java/io/karatelabs/core/Runner.java","lineNumber":681,"sourceCode":"        }\n\n        /**\n         * Add a run listener factory by class name (supports no-arg constructor).\n         * Used for CLI --listener-factory option.\n         * @param className fully qualified class name\n         */\n        public Builder listenerFactory(String className) {\n            if (className != null && !className.isEmpty()) {\n                try {\n                    Class<?> clazz = Class.forName(className);\n                    Object instance = clazz.getDeclaredConstructor().newInstance();\n                    if (instance instanceof RunListenerFactory factory) {\n                        listenerFactories.add(factory);\n                    } else if (instance instanceof RunListener listener) {\n                        // If it's a RunListener, wrap it in a factory that returns the same instance\n                        listeners.add(listener);\n                    } else {\n                        throw new IllegalArgumentException(\n                                \"Class \" + className + \" must implement RunListenerFactory or RunListener\");\n                    }\n                } catch (ClassNotFoundException e) {\n                    throw new IllegalArgumentException(\"Class not found: \" + className, e);\n                } catch (Exception e) {\n                    throw new IllegalArgumentException(\"Failed to instantiate \" + className, e);\n                }\n            }\n            return this;\n        }\n\n        /**\n         * Add a result listener for streaming test results.\n         */\n        public Builder resultListener(ResultListener listener) {\n            if (listener != null) {\n                resultListeners.add(listener);\n            }","sourceCodeStart":663,"sourceCodeEnd":699,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/core/Runner.java#L663-L699","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\npublic class MyListener { }\n// after\nimport io.karatelabs.core.RunListener;\npublic class MyListener implements RunListener { /* callbacks */ }","handlingStrategy":"type-guard","validationCode":"Class<?> c = Class.forName(name);\nif (!RunListener.class.isAssignableFrom(c) && !RunListenerFactory.class.isAssignableFrom(c))\n    throw new IllegalArgumentException(name + \" must implement RunListener or RunListenerFactory\");","typeGuard":"boolean isKarateListener(Class<?> c) { return RunListener.class.isAssignableFrom(c) || RunListenerFactory.class.isAssignableFrom(c); }","tryCatchPattern":"try { builder.listener(name); } catch (IllegalArgumentException e) { if (e.getMessage().endsWith(\"must implement RunListenerFactory or RunListener\")) { log.error(\"{}: add 'implements RunListener'\", name); } throw e; }","preventionTips":["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"],"tags":["runner","listeners","configuration","type-mismatch"],"backgroundTag":"invalid-argument-value","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}