jenkinsci/jenkins · error · IOException

Failed to delete {0}

Error message

Failed to delete {0}

What it means

IOException thrown by PluginWrapper.enable() when disableFile.delete() returns false — the disable marker file (.disable) exists but could not be deleted, preventing the plugin from being enabled on next restart. Common causes: insufficient filesystem permissions, the file is locked by another process, or the parent directory is read-only.

Source

Thrown at core/src/main/java/hudson/PluginWrapper.java:775

    public void releaseClassLoader() {
        if (classLoader instanceof Closeable)
            try {
                ((Closeable) classLoader).close();
            } catch (IOException e) {
                LOGGER.log(WARNING, "Failed to shut down classloader", e);
            }
    }

    /**
     * Enables this plugin next time Jenkins runs.
     */
    public void enable() throws IOException {
        if (!disableFile.exists()) {
            LOGGER.log(Level.FINEST, "Plugin {0} has been already enabled. Skipping the enable() operation", getShortName());
            return;
        }
        if (!disableFile.delete())
            throw new IOException("Failed to delete " + disableFile);
    }

    /**
     * Disables this plugin next time Jenkins runs. As it doesn't check anything, it's recommended to use the method
     * {@link #disable(PluginDisableStrategy)}
     * @deprecated use {@link #disable(PluginDisableStrategy)}
     */
    @Deprecated //see https://issues.jenkins.io/browse/JENKINS-27177
    public void disable() throws IOException {
        disableWithoutCheck();
    }

    /**
     * Disable a plugin wihout checking any dependency. Only add the disable file.
     */
    private void disableWithoutCheck() throws IOException {
        try (OutputStream os = Files.newOutputStream(disableFile.toPath())) {
            // creates an empty file

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Verify the Jenkins process has write/delete permission on the plugins directory and the .disable file.
  2. On Windows, close any process locking the file (antivirus, indexer, backup).
  3. In Docker, ensure the container UID matches the plugins directory owner.
  4. Manually delete the .disable file and retry.

Example fix

// before
wrapper.enable();

// after — check permissions and handle failure
File disableFile = new File(wrapper.getShortName() + ".jpi.disable");
if (disableFile.exists() && !disableFile.delete()) {
    disableFile.setWritable(true, false);
    if (!disableFile.delete()) {
        throw new IOException("Cannot remove .disable file; check permissions: " + disableFile);
    }
}
Defensive patterns

Strategy: validation

Validate before calling

if (disableFile.exists() && !disableFile.canWrite()) {
    disableFile.setWritable(true, false);
}
if (disableFile.exists() && !disableFile.delete()) {
    throw new IOException("Cannot delete .disable file; check permissions: " + disableFile);
}

Try / catch

try {
    wrapper.enable();
} catch (IOException e) {
    if (e.getMessage().startsWith("Failed to delete")) {
        File disableFile = new File(wrapper.getShortName() + ".jpi.disable");
        disableFile.setWritable(true, false);
        if (!disableFile.delete()) {
            throw new IOException("Permission denied on plugins directory; cannot enable plugin.", e);
        }
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling plugin.enable() when the .disable file exists but File.delete() fails (returns false) due to permissions, locks, or filesystem errors.

Common situations: Jenkins running as a user without write access to the plugins directory (common in Docker with wrong UID/GID mapping), the .disable file locked by an antivirus or backup process on Windows, or a read-only mount for the plugins directory.

Related errors


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