jenkinsci/jenkins · error · IOException

Failed to install {0} plugin

Error message

Failed to install {0} plugin

What it means

Wraps any exception thrown during plugin installation (strategy.createPluginWrapper, batch.add, or start) into an IOException with the plugin short name. The original cause is attached. The failed plugin is added to failedPlugins, removed from activePlugins and plugins, and its classloader is released before rethrowing.

Source

Thrown at core/src/main/java/hudson/PluginManager.java:985

            // TODO antimodular; perhaps should have a PluginListener to complement ExtensionListListener?
            CustomClassFilter.Contributed.load();

            try {
                p.resolvePluginDependencies();
                strategy.load(p);

                if (batch != null) {
                    batch.add(p);
                } else {
                    start(List.of(p));
                }

            } catch (Exception e) {
                failedPlugins.add(new FailedPlugin(p, e));
                activePlugins.remove(p);
                plugins.remove(p);
                p.releaseClassLoader();
                throw new IOException("Failed to install " + sn + " plugin", e);
            }

            LOGGER.log(FINE, "Plugin {0}:{1} dynamically {2}", new Object[] {p.getShortName(), p.getVersion(), batch != null ? "loaded but not yet started" : "installed"});
        }
    }

    @Restricted(NoExternalUse.class)
    public void start(List<PluginWrapper> plugins) throws Exception {
      try (ACLContext context = ACL.as2(ACL.SYSTEM2)) {
        Map<String, PluginWrapper> pluginsByName = plugins.stream().collect(Collectors.toMap(PluginWrapper::getShortName, p -> p));

        // recalculate dependencies of plugins optionally depending the newly deployed ones.
        for (PluginWrapper depender : this.plugins) {
            if (plugins.contains(depender)) {
                // skip itself.
                continue;
            }
            for (Dependency d : depender.getOptionalDependencies()) {

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Inspect the wrapped cause (e.getCause()) for the specific failure reason.
  2. Verify the plugin archive integrity (valid ZIP/HPI structure, not truncated).
  3. Check plugin dependency compatibility against currently installed plugins.
  4. Review Jenkins logs for the plugin's onLoad/start stack trace.

Example fix

// before
try {
    pluginManager.dynamicLoad(arc);
} catch (IOException e) {
    // generic handling
}

// after — extract root cause
try {
    pluginManager.dynamicLoad(arc);
} catch (IOException e) {
    Throwable root = e.getCause();
    listener.error("Plugin install failed: " + root.getMessage());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate archive before deploying
if (!fileName.endsWith(".jpi") && !fileName.endsWith(".hpi")) {
    throw new IllegalArgumentException("Not a valid plugin archive: " + fileName);
}
try (JarFile jf = new JarFile(arc)) {
    if (jf.getManifest() == null) {
        throw new IllegalArgumentException("Plugin archive has no manifest.");
    }
}

Try / catch

try {
    pluginManager.dynamicLoad(arc);
} catch (IOException e) {
    Throwable cause = e.getCause();
    listener.error("Failed to install " + sn + ": " + cause.getClass().getSimpleName() + ": " + cause.getMessage());
    // optionally: pluginManager.getFailedPlugins() for diagnostics
}

Prevention

When it happens

Trigger: An exception occurs anywhere in the dynamic load pipeline: createPluginWrapper fails (corrupt archive), batch.add fails (dependency conflict), or start fails (plugin initialization error).

Common situations: Corrupt or incompatible .jpi/.hpi file uploaded, plugin version conflicts with existing dependencies, ClassNotFoundException during plugin initialization, or a plugin's onLoad/start method throwing.

Related errors


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