{"record":{"id":"4a2b979b7ad5c554","repo":"elastic/elasticsearch","slug":"could-not-remove-the-following-files-in-the-order","errorCode":null,"errorMessage":"could not remove the following files (in the order of attempts):\n   {}: {}\n...","messagePattern":"could not remove the following files \\(in the order of attempts\\):\n   (.+?): (.+?)\n\\.\\.\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"libs/core/src/main/java/org/elasticsearch/core/IOUtils.java","lineNumber":203,"sourceCode":"\n                }\n            }\n        }\n    }\n\n    /**\n     * Deletes one or more files or directories (and everything underneath it).\n     *\n     * @throws IOException if any of the given files (or their sub-hierarchy files in case of directories) cannot be removed.\n     */\n    public static void rm(final Path... locations) throws IOException {\n        final LinkedHashMap<Path, Throwable> unremoved = rm(new LinkedHashMap<>(), locations);\n        if (unremoved.isEmpty() == false) {\n            final StringBuilder b = new StringBuilder(\"could not remove the following files (in the order of attempts):\\n\");\n            for (final Map.Entry<Path, Throwable> kv : unremoved.entrySet()) {\n                b.append(\"   \").append(kv.getKey().toAbsolutePath()).append(\": \").append(kv.getValue()).append(\"\\n\");\n            }\n            throw new IOException(b.toString());\n        }\n    }\n\n    private static LinkedHashMap<Path, Throwable> rm(final LinkedHashMap<Path, Throwable> unremoved, final Path... locations) {\n        if (locations != null) {\n            for (final Path location : locations) {\n                // TODO: remove this leniency\n                if (location != null && Files.exists(location)) {\n                    try {\n                        Files.walkFileTree(location, new FileVisitor<Path>() {\n                            @Override\n                            public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {\n                                return FileVisitResult.CONTINUE;\n                            }\n\n                            @Override\n                            public FileVisitResult postVisitDirectory(final Path dir, final IOException impossible) throws IOException {\n                                assert impossible == null;","sourceCodeStart":185,"sourceCodeEnd":221,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/core/src/main/java/org/elasticsearch/core/IOUtils.java#L185-L221","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nIOUtils.rm(indexDir); // throws — reader still open\n\n// after — close readers first\ntry (Directory dir = FSDirectory.open(indexDir);\n     IndexReader r = DirectoryReader.open(dir)) {\n    // use r\n}\nIOUtils.rm(indexDir);","handlingStrategy":"try-catch","validationCode":"// Best-effort pre-check: confirm no locks/permissions issues before rm\nvoid safeRm(Path p) throws IOException {\n    if (!Files.exists(p)) return;\n    try {\n        IOUtils.rm(p);\n    } catch (IOException e) {\n        // log details and rethrow or degrade\n    }\n}","typeGuard":"static boolean isLikelyRemovable(Path p) {\n    try {\n        return Files.isWritable(p) && Files.exists(p);\n    } catch (SecurityException e) {\n        return false;\n    }\n}","tryCatchPattern":"try {\n    IOUtils.rm(dir);\n} catch (IOException e) {\n    // parse the per-path causes; if access denied, fix permissions and retry;\n    // if file in use, close handles and retry once. Otherwise surface the failure.\n    log.warn(\"rm failed: {}\", e.getMessage());\n    throw e;\n}","preventionTips":["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."],"tags":["io","filesystem","cleanup","lucene","windows","permissions","elasticsearch-core"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}