jenkinsci/jenkins · error · IOException

{} doesn't extend from hudson.Plugin

Error message

{} doesn't extend from hudson.Plugin

What it means

IOException thrown during plugin instantiation when the plugin class named in the manifest loads and instantiates successfully but the resulting object is not an instance of hudson.Plugin. Jenkins requires the declared plugin class to extend hudson.Plugin so its lifecycle can be managed.

Source

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

    }

    @Override
    public void load(PluginWrapper wrapper) throws IOException {
        // override the context classloader. This no longer makes sense,
        // but it is left for the backward compatibility
        ClassLoader old = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(wrapper.classLoader);
        try {
            String className = wrapper.getPluginClass();
            if (className == null) {
                // use the default dummy instance
                wrapper.setPlugin(new DummyImpl());
            } else {
                try {
                    Class<?> clazz = wrapper.classLoader.loadClass(className);
                    Object o = clazz.getDeclaredConstructor().newInstance();
                    if (!(o instanceof Plugin)) {
                        throw new IOException(className + " doesn't extend from hudson.Plugin");
                    }
                    wrapper.setPlugin((Plugin) o);
                } catch (LinkageError | ClassNotFoundException e) {
                    throw new IOException("Unable to load " + className + " from " + wrapper.getShortName(), e);
                } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                    throw new IOException("Unable to create instance of " + className + " from " + wrapper.getShortName(), e);
                }
            }

            // initialize plugin
            try {
                Plugin plugin = wrapper.getPluginOrFail();
                plugin.setServletContext(pluginManager.context);
                startPlugin(wrapper);
            } catch (Throwable t) {
                // gracefully handle any error in plugin.
                throw new IOException("Failed to initialize", t);
            }

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Make the class referenced by the manifest Plugin-Class extend hudson.Plugin (or remove the Plugin-Class attribute to use the default DummyImpl).
  2. If the plugin has no lifecycle need, delete the Plugin-Class entry from the manifest instead of pointing at a non-Plugin class.
  3. Rebuild and reinstall the plugin after the change.

Example fix

// before
public class MyPlugin {
  ...
}
// after
import hudson.Plugin;
public class MyPlugin extends Plugin {
  ...
}
Defensive patterns

Strategy: type-guard

Validate before calling

Class<?> clazz = wrapper.classLoader.loadClass(className);
if (!Plugin.class.isAssignableFrom(clazz)) {
    throw new IOException(className + " must extend hudson.Plugin");
}

Type guard

boolean isPluginClass(Class<?> c) {
    return c != null && hudson.Plugin.class.isAssignableFrom(c);
}

Try / catch

try {
    Object o = clazz.getDeclaredConstructor().newInstance();
    if (!(o instanceof Plugin)) {
        // instruct plugin author to extend hudson.Plugin
    }
} catch (IOException e) {
    // report the manifest Plugin-Class value
}

Prevention

When it happens

Trigger: A plugin manifest's Plugin-Class attribute points at a class that does not extend hudson.Plugin (e.g. a utility class, a Jelly-bound object, or a class extending jenkins.model.Jenkins by mistake).

Common situations: Plugin developer set Plugin-Class to the wrong class; refactored the main plugin class to no longer extend Plugin; copied a manifest from another plugin without updating Plugin-Class.

Related errors


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