jenkinsci/jenkins · critical · IllegalStateException

Refusing to load the Jenkins test harness in production (via

Error message

Refusing to load the Jenkins test harness in production (via {})

What it means

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.

Source

Thrown at core/src/main/java/hudson/ClassicPluginStrategy.java:300

     */
    @Deprecated(since = "2.459")
    protected ClassLoader createClassLoader(List<File> paths, ClassLoader parent, Attributes atts) throws IOException {
        // generate a legacy id so at least we can track to something
        return createClassLoader("unidentified-" + UUID.randomUUID(), paths, parent, atts);
    }

    /**
     * Creates a  classloader that can load all the specified jar files and delegate to the given parent.
     * @since 2.459
     */
    protected ClassLoader createClassLoader(String name, List<File> paths, ClassLoader parent, Attributes atts) throws IOException {
        boolean usePluginFirstClassLoader =
                atts != null && Boolean.parseBoolean(atts.getValue("PluginFirstClassLoader"));

        List<URL> urls = new ArrayList<>();
        for (File path : paths) {
            if (path.getName().startsWith("jenkins-test-harness")) {
                throw new IllegalStateException("Refusing to load the Jenkins test harness in production (via "
                        + atts.getValue("Short-Name") + ")");
            }
            urls.add(path.toURI().toURL());
        }
        URLClassLoader2 classLoader;
        if (usePluginFirstClassLoader) {
            classLoader = new PluginFirstClassLoader2(name, urls.toArray(new URL[0]), parent);
        } else {
            classLoader = new URLClassLoader2(name, urls.toArray(new URL[0]), parent);
        }
        return classLoader;
    }

    /**
     * Computes the classloader that takes the class masking into account.
     *
     * <p>
     * This mechanism allows plugins to have their own versions for libraries that core bundles.

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. 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).
  2. Remove the offending jenkins-test-harness-*.jar from the plugin archive and reinstall.
  3. Run 'mvn dependency:tree' on the plugin to find what transitively brings in the test harness and exclude it.

Example fix

<!-- before: scope leaked into the package -->
<dependency>
  <groupId>org.jenkins-ci.main</groupId>
  <artifactId>jenkins-test-harness</artifactId>
</dependency>
<!-- after -->
<dependency>
  <groupId>org.jenkins-ci.main</groupId>
  <artifactId>jenkins-test-harness</artifactId>
  <scope>test</scope>
</dependency>
Defensive patterns

Strategy: validation

Validate before calling

// At build time, ensure no jenkins-test-harness jar is packaged
try (JarFile jf = new JarFile(pluginArchive)) {
    if (jf.getEntry("WEB-INF/lib/jenkins-test-harness") != null
            || jf.stream().anyMatch(e -> e.getName().contains("jenkins-test-harness"))) {
        throw new IllegalStateException("Plugin packages jenkins-test-harness; fix the POM");
    }
}

Try / catch

try {
    strategy.createClassLoader(name, paths, parent, atts);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("jenkins-test-harness")) {
        // reject the plugin build and fix dependency scoping
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jenkinsci/jenkins@2e228ff40b (2026-08-14). Data as JSON: /api/errors/f01d1147e29d789c. Report an issue: GitHub.