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 fileView on GitHub (pinned to 2e228ff40b)
Solutions
- Verify the Jenkins process has write/delete permission on the plugins directory and the .disable file.
- On Windows, close any process locking the file (antivirus, indexer, backup).
- In Docker, ensure the container UID matches the plugins directory owner.
- 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
- Ensure the Jenkins process UID/GID has write access to the plugins directory.
- In Docker, match the container user to the plugins directory owner.
- On Windows, exclude the Jenkins home from antivirus real-time scanning.
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
- Failed to mkdirs: ${remote}
- Failed to create a temporary directory in ${dir}
- Failed to set the timestamp of ${f} to ${timestamp}
- Failed to fully read {0}
- Invalid mode: {}
AI-assisted analysis of jenkinsci/jenkins@2e228ff40b (2026-08-14).
Data as JSON: /api/errors/8033c524d3ed940a.
Report an issue: GitHub.