elastic/elasticsearch · error · IOException
could not remove the following files (in the order of attemp
Error message
could not remove the following files (in the order of attempts):
{}: {}
... What it means
Thrown by IOUtils.rm after it has attempted to delete every file and directory under the given paths. rm collects per-path failures into a LinkedHashMap (path -> Throwable) and, if any remain, raises a single IOException listing each failed path with its cause. The multi-line message preserves attempt order so the most common root cause (usually permissions or a held file handle) is visible first.
Source
Thrown at libs/core/src/main/java/org/elasticsearch/core/IOUtils.java:203
}
}
}
}
/**
* Deletes one or more files or directories (and everything underneath it).
*
* @throws IOException if any of the given files (or their sub-hierarchy files in case of directories) cannot be removed.
*/
public static void rm(final Path... locations) throws IOException {
final LinkedHashMap<Path, Throwable> unremoved = rm(new LinkedHashMap<>(), locations);
if (unremoved.isEmpty() == false) {
final StringBuilder b = new StringBuilder("could not remove the following files (in the order of attempts):\n");
for (final Map.Entry<Path, Throwable> kv : unremoved.entrySet()) {
b.append(" ").append(kv.getKey().toAbsolutePath()).append(": ").append(kv.getValue()).append("\n");
}
throw new IOException(b.toString());
}
}
private static LinkedHashMap<Path, Throwable> rm(final LinkedHashMap<Path, Throwable> unremoved, final Path... locations) {
if (locations != null) {
for (final Path location : locations) {
// TODO: remove this leniency
if (location != null && Files.exists(location)) {
try {
Files.walkFileTree(location, new FileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(final Path dir, final IOException impossible) throws IOException {
assert impossible == null;View on GitHub (pinned to db6a809a66)
Solutions
- Close any IndexReader, Directory, or channel that may hold the path before calling rm (use try-with-resources).
- Read the listed cause for the first failing path — it typically identifies the real problem (access denied, file in use).
- On Windows, ensure no mmap'd segment is still mapped; prefer IOUtils.FS.setMmapEnabled(false) in tests that delete aggressively.
- Check filesystem permissions and ownership of the listed paths; run as the owner or fix chmod/chown.
- If partial deletion is acceptable, wrap rm in a try and fall back to best-effort cleanup, but treat the failure as a leak signal.
Example fix
// before
IOUtils.rm(indexDir); // throws — reader still open
// after — close readers first
try (Directory dir = FSDirectory.open(indexDir);
IndexReader r = DirectoryReader.open(dir)) {
// use r
}
IOUtils.rm(indexDir); Defensive patterns
Strategy: try-catch
Validate before calling
// Best-effort pre-check: confirm no locks/permissions issues before rm
void safeRm(Path p) throws IOException {
if (!Files.exists(p)) return;
try {
IOUtils.rm(p);
} catch (IOException e) {
// log details and rethrow or degrade
}
} Type guard
static boolean isLikelyRemovable(Path p) {
try {
return Files.isWritable(p) && Files.exists(p);
} catch (SecurityException e) {
return false;
}
} Try / catch
try {
IOUtils.rm(dir);
} catch (IOException e) {
// parse the per-path causes; if access denied, fix permissions and retry;
// if file in use, close handles and retry once. Otherwise surface the failure.
log.warn("rm failed: {}", e.getMessage());
throw e;
} Prevention
- Close all IndexReaders/Directories/channels in try-with-resources before rm.
- On Windows, disable mmap for tests that aggressively delete index dirs.
- Read the per-path cause list to identify the real blocker.
- Treat repeated rm failures as a resource-leak signal, not a nuisance.
When it happens
Trigger: Calling IOUtils.rm(path...) on a directory tree where one or more files cannot be deleted: open file handles (mmap, Lucene index reader), permission denied, read-only filesystem, or a path concurrently modified by another process. The IOException is thrown only after the full walk completes.
Common situations: Test teardown deleting a temp index directory while an IndexReader/Directory is still open. Snapshots or recovery holding handles. Windows notoriously holds files open. Container/CI runs with an unprivileged user against a root-owned path. NFS/network filesystem locking.
Related errors
- {}
- Failed to create parent directory '%s' when creating directo
- Failed to create directory '%s'
- Failed to list existing plugins
- Failed to list entitlement jars in: ${dir}
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/4a2b979b7ad5c554.
Report an issue: GitHub.