apache/druid · error · org.apache.druid.java.util.common.IAE

Cannot list contents of

Error message

Cannot list contents of [%s] since it is not a directory

What it means

LocalFileStorageConnector.listDir throws this IllegalArgumentException when the given path resolves to an existing file rather than a directory. The connector first checks existence, then verifies the path is a directory before calling File.listFiles(). It is a caller-input validation error.

Solutions

  1. Fix the caller to pass a directory path, not a file path
  2. Verify the path on disk with ls -la and correct the directory layout
  3. Add an isDirectory check in the caller before invoking listDir

Example fix

// before
connector.listDir("taskDir/task-123/index.zip");
// after
connector.listDir("taskDir/task-123");
Defensive patterns

Strategy: validation

Validate before calling

File dir = new File(basePath, dirName);
if (!dir.isDirectory()) {
  throw new IllegalArgumentException(dirName + " is not a directory");
}
connector.listDir(dirName);

Type guard

static boolean isDirectory(File f) { return f != null && f.isDirectory(); }

Try / catch

try { connector.listDir(dir); } catch (IllegalArgumentException e) { logger.error(e, "not a directory: %s", dir); }

Prevention

When it happens

Trigger: Calling listDir(dirName) with a path that points to a regular file (or symlink to a file) that exists under the connector's basePath. Example: listDir("segments/wiki/2010-01-01T00:00:00.000Z_2010-01-01T01:00:00.000Z/0") where the final element is a file, not a folder.

Common situations: Passing a full file path (including segment descriptor file) to a directory-listing API; a config that mixes up the logs/lock/task directory root with a file inside it; stale directory replaced by a file after upgrade or manual cleanup.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/0d5df886e41b870d. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/storage/local/LocalFileStorageConnector.java:161

   * @param dirName path
   * @throws IOException thrown in case of errors.
   */
  @Override
  public void deleteRecursively(String dirName) throws IOException
  {
    log.debug("Deleting directory at path: [%s]", dirName);
    FileUtils.deleteDirectory(fileWithBasePath(dirName));
  }

  @Override
  public Iterator<String> listDir(String dirName)
  {
    File directory = fileWithBasePath(dirName);
    if (!directory.exists()) {
      throw new IAE("No directory exists on path [%s]", dirName);
    }
    if (!directory.isDirectory()) {
      throw new IAE("Cannot list contents of [%s] since it is not a directory", dirName);
    }
    File[] files = directory.listFiles();
    if (files == null) {
      throw new ISE("Unable to fetch the file list from the path [%s]", dirName);
    }
    return Arrays.stream(files).map(File::getName).iterator();
  }

  public File getBasePath()
  {
    return basePath;
  }

  private File fileWithBasePath(String path)
  {
    return new File(basePath, path);
  }

View on GitHub (pinned to 9b90983fd2)