apache/pulsar · error · IOException

Unable to list directory content in: ${directory}

Error message

Unable to list directory content in: ${directory}

What it means

FileUtils.deleteFilesInDirectory lists the directory via File.listFiles() before deleting entries; listFiles() returns null not only for non-directories but also when an I/O error occurs while listing, in which case the method throws IOException 'Unable to list directory content in: <abs path>'. This guards against silently treating an unreadable directory as empty.

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/nar/FileUtils.java:153

     * this is printed at warn to the given logger.
     *
     * @param directory to delete contents of
     * @param filter if null then no filter is used
     * @param recurse will look for contents of sub directories.
     * @param deleteEmptyDirectories default is false; if true will delete
     * directories found that are empty
     * @throws IOException if abstract pathname does not denote a directory, or
     * if an I/O error occurs
     */
    public static void deleteFilesInDirectory(
        final File directory, final FilenameFilter filter,
        final boolean recurse, final boolean deleteEmptyDirectories) throws IOException {
        // ensure the specified directory is actually a directory and that it exists
        if (null != directory && directory.isDirectory()) {
            final File ingestFiles[] = directory.listFiles();
            if (ingestFiles == null) {
                // null if abstract pathname does not denote a directory, or if an I/O error occurs
                throw new IOException("Unable to list directory content in: " + directory.getAbsolutePath());
            }
            for (File ingestFile : ingestFiles) {
                boolean process = (filter == null) ? true : filter.accept(directory, ingestFile.getName());
                if (ingestFile.isFile() && process) {
                    FileUtils.deleteFile(ingestFile, 3);
                }
                if (ingestFile.isDirectory() && recurse) {
                    FileUtils.deleteFilesInDirectory(ingestFile, filter, recurse, deleteEmptyDirectories);
                    String[] ingestFileList = ingestFile.list();
                    if (deleteEmptyDirectories && ingestFileList != null && ingestFileList.length == 0) {
                        FileUtils.deleteFile(ingestFile, 3);
                    }
                }
            }
        }
    }

    /**

View on GitHub (pinned to 820761864e)

Solutions

  1. Verify the process can list the path (ls as the service user).
  2. Check dmesg/mount status for stale NFS handles and remount the share.
  3. Re-grant read+execute permission on the directory (readdir needs r and x).
  4. Ensure no external process deletes/recreates the directory concurrently.

Example fix

// before
FileUtils.deleteFilesInDirectory(dir, filter, true, true);
// after
if (dir != null && dir.canRead()) {
    FileUtils.deleteFilesInDirectory(dir, filter, true, true);
} else {
    log.warn("skipping cleanup, cannot read {}", dir);
}
Defensive patterns

Strategy: retry

Validate before calling

if (directory != null && directory.isDirectory() && !directory.canRead()) {
    log.warn("Skipping cleanup: cannot read {}", directory);
    return;
}

Try / catch

try {
    FileUtils.deleteFilesInDirectory(dir, null, true, true);
} catch (IOException e) {
    if (e.getMessage().startsWith("Unable to list directory content")) {
        // transient IO/race: log and retry later, don't crash the cleanup loop
        log.warn("Directory listing failed for {}, will retry next cycle", dir, e);
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: deleteFilesInDirectory called on a directory that exists (isDirectory() true) but whose entries cannot be enumerated: permission denied on readdir, the directory vanishing mid-scan, or filesystem/IO errors (NFS stale handle).

Common situations: Cleanup tasks for NAR unpack directories racing with external deletion, stale NFS handles after a server restart, or permission changes applied while the process runs.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/3df6d7c0cd249319. Report an issue: GitHub.