{"record":{"id":"f01d1147e29d789c","repo":"jenkinsci/jenkins","slug":"refusing-to-load-the-jenkins-test-harness-in-produ","errorCode":null,"errorMessage":"Refusing to load the Jenkins test harness in production (via {})","messagePattern":"Refusing to load the Jenkins test harness in production \\(via (.+?)\\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"core/src/main/java/hudson/ClassicPluginStrategy.java","lineNumber":300,"sourceCode":"     */\n    @Deprecated(since = \"2.459\")\n    protected ClassLoader createClassLoader(List<File> paths, ClassLoader parent, Attributes atts) throws IOException {\n        // generate a legacy id so at least we can track to something\n        return createClassLoader(\"unidentified-\" + UUID.randomUUID(), paths, parent, atts);\n    }\n\n    /**\n     * Creates a  classloader that can load all the specified jar files and delegate to the given parent.\n     * @since 2.459\n     */\n    protected ClassLoader createClassLoader(String name, List<File> paths, ClassLoader parent, Attributes atts) throws IOException {\n        boolean usePluginFirstClassLoader =\n                atts != null && Boolean.parseBoolean(atts.getValue(\"PluginFirstClassLoader\"));\n\n        List<URL> urls = new ArrayList<>();\n        for (File path : paths) {\n            if (path.getName().startsWith(\"jenkins-test-harness\")) {\n                throw new IllegalStateException(\"Refusing to load the Jenkins test harness in production (via \"\n                        + atts.getValue(\"Short-Name\") + \")\");\n            }\n            urls.add(path.toURI().toURL());\n        }\n        URLClassLoader2 classLoader;\n        if (usePluginFirstClassLoader) {\n            classLoader = new PluginFirstClassLoader2(name, urls.toArray(new URL[0]), parent);\n        } else {\n            classLoader = new URLClassLoader2(name, urls.toArray(new URL[0]), parent);\n        }\n        return classLoader;\n    }\n\n    /**\n     * Computes the classloader that takes the class masking into account.\n     *\n     * <p>\n     * This mechanism allows plugins to have their own versions for libraries that core bundles.","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/jenkinsci/jenkins/blob/2e228ff40b14dbc8b14ffbc6edf0e4383cf744fc/core/src/main/java/hudson/ClassicPluginStrategy.java#L282-L318","documentation":"IllegalStateException thrown by ClassicPluginStrategy.createClassLoader when a JAR on the plugin classpath has a name starting with 'jenkins-test-harness'. Jenkins deliberately refuses to load the test harness into a production controller because it pulls in test-only dependencies and assertions that break production behavior.","triggerScenarios":"A plugin bundles (or its lib directory contains) jenkins-test-harness-*.jar as a dependency, so it ends up on the plugin's classpath at load time.","commonSituations":"Plugin build accidentally includes the test scope dependency in the packaged .jpi (scope leakage); a dependency pulled in transitively; a developer reused a fat-jar built with test classes.","solutions":["Fix the plugin POM so jenkins-test-harness is test-scoped and excluded from the assembled .jpi (check the WEB-INF/lib of the packaged plugin).","Remove the offending jenkins-test-harness-*.jar from the plugin archive and reinstall.","Run 'mvn dependency:tree' on the plugin to find what transitively brings in the test harness and exclude it."],"exampleFix":"<!-- before: scope leaked into the package -->\n<dependency>\n  <groupId>org.jenkins-ci.main</groupId>\n  <artifactId>jenkins-test-harness</artifactId>\n</dependency>\n<!-- after -->\n<dependency>\n  <groupId>org.jenkins-ci.main</groupId>\n  <artifactId>jenkins-test-harness</artifactId>\n  <scope>test</scope>\n</dependency>","handlingStrategy":"validation","validationCode":"// At build time, ensure no jenkins-test-harness jar is packaged\ntry (JarFile jf = new JarFile(pluginArchive)) {\n    if (jf.getEntry(\"WEB-INF/lib/jenkins-test-harness\") != null\n            || jf.stream().anyMatch(e -> e.getName().contains(\"jenkins-test-harness\"))) {\n        throw new IllegalStateException(\"Plugin packages jenkins-test-harness; fix the POM\");\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    strategy.createClassLoader(name, paths, parent, atts);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"jenkins-test-harness\")) {\n        // reject the plugin build and fix dependency scoping\n    }\n}","preventionTips":["Scope jenkins-test-harness as 'test' in plugin POMs.","Run dependency:tree in CI to catch scope leakage.","Inspect the packaged .jpi's WEB-INF/lib before release."],"tags":["jenkins","plugin-loading","classloader","test-harness"],"backgroundTag":null,"analyzedSha":"2e228ff40b14dbc8b14ffbc6edf0e4383cf744fc","analyzedAt":"2026-08-14T07:07:15.274Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}