{"record":{"id":"117e80b6b866904e","repo":"apache/pulsar","slug":"unable-to-delete-file","errorCode":null,"errorMessage":"Unable to delete ${file}","messagePattern":"Unable to delete (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/nar/FileUtils.java","lineNumber":191,"sourceCode":"     *\n     * @param files to delete\n     * @param recurse will recurse\n     * @throws IOException if issues deleting files\n     */\n    public static void deleteFiles(final Collection<File> files, final boolean recurse) throws IOException {\n        for (final File file : files) {\n            FileUtils.deleteFile(file, recurse);\n        }\n    }\n\n    public static void deleteFile(final File file, final boolean recurse) throws IOException {\n        final File[] list = file.listFiles();\n        if (file.isDirectory() && recurse && list != null) {\n            FileUtils.deleteFiles(Arrays.asList(list), recurse);\n        }\n        //now delete the file itself regardless of whether it is plain file or a directory\n        if (!FileUtils.deleteFile(file, 5)) {\n            throw new IOException(\"Unable to delete \" + file.getAbsolutePath());\n        }\n    }\n\n    public static void sleepQuietly(final long millis) {\n        try {\n            Thread.sleep(millis);\n        } catch (final InterruptedException ex) {\n            /* do nothing */\n        }\n    }\n\n    public static boolean mayBeANarArchive(File jarFile) {\n        try (ZipFile zipFile = new ZipFile(jarFile);) {\n            ZipEntry entry = zipFile.getEntry(\"META-INF/bundled-dependencies\");\n            if (entry == null || !entry.isDirectory()) {\n                log.info().attr(\"jarFile\", jarFile)\n                        .log(\"Jar file does not contain META-INF/bundled-dependencies,\"\n                                + \" it is not a NAR file\");","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/nar/FileUtils.java#L173-L209","documentation":"FileUtils.deleteFile(File, recurse) deletes a file or directory tree; after recursively deleting children it calls the retrying internal deleteFile(file, 5), and if that still fails it throws IOException 'Unable to delete <abs path>'. Deletion typically fails because something still holds the file open (unlink returns EBUSY/EPERM) or the parent directory lacks write permission.","triggerScenarios":"deleteFile called on a path that is locked/open by another process, is a non-empty directory without recurse=true, or whose parent directory is not writable by the process user.","commonSituations":"Windows file locks (antivirus/indexers holding NAR jars), a running JVM keeping an unpacked jar mapped, deleting NAR unpack dirs while the broker still has them loaded, or root-owned leftovers.","solutions":["Find and stop the process holding the file open (lsof | grep <path> on Linux; handle.exe on Windows).","Ensure the parent directory is writable (chmod/chown).","Pass recurse=true for non-empty directories.","Retry after the holder releases the lock — the internal code already retries 5 times."],"exampleFix":"// before\nFileUtils.deleteFile(new File(\"/tmp/nar/my.nar-unpacked\")); // non-empty dir\n// after\nFileUtils.deleteFile(new File(\"/tmp/nar/my.nar-unpacked\"), true); // recursive","handlingStrategy":"retry","validationCode":"if (file.exists() && !file.getParentFile().canWrite()) {\n    throw new IllegalStateException(\"Parent not writable, delete will fail: \" + file.getParent());\n}","typeGuard":null,"tryCatchPattern":"try {\n    FileUtils.deleteFile(file, true);\n} catch (IOException e) {\n    if (e.getMessage().startsWith(\"Unable to delete\")) {\n        // commonly a file lock: schedule retry after holders release\n        log.warn(\"Delete failed for {}; likely locked by another process\", file, e);\n        return;\n    }\n    throw e;\n}","preventionTips":["Close streams/locks on NAR jars and unpacked files before deleting them","On Windows, exclude service directories from antivirus/indexer locking","Only call deleteFile on directories with recurse=true","Run deletion under the same user that created the files"],"tags":["filesystem","io","file-delete","file-lock"],"backgroundTag":"file-delete-failed","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}