apache/druid · error · IOException

directory[ ] is not a directory

Error message

directory[%s] is not a directory

What it means

CompressionUtils.zip compresses a directory tree into a ZIP stream and validates upfront that the given File is actually a directory; if it is a regular file, symlink target, or does not exist as a directory, it throws this IOException. The check prevents the subsequent listFiles() null-dereference and misdirected zipping.

Solutions

  1. Verify the path is a directory before calling: file.isDirectory() (and exists())
  2. Point zip() at the parent directory if the intent was to archive a single file — or use file-copy helpers for single files
  3. Fix the configured directory path in job/spec config that resolved to a file
  4. Create the directory if it is legitimately missing (FileUtils.mkdirhier) before zipping

Example fix

// before
CompressionUtils.zip(new File("/data/segments/segment-file"), out); // throws
// after
File dir = new File("/data/segments/my-segment");
if (!dir.isDirectory()) {
  throw new IllegalArgumentException("Expected a directory: " + dir);
}
CompressionUtils.zip(dir, out);
Defensive patterns

Strategy: validation

Validate before calling

File dir = new File(path);
if (!dir.isDirectory()) {
  throw new IllegalArgumentException("Not a directory: " + path);
}

Try / catch

try {
  CompressionUtils.zip(dir, out);
} catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().contains("is not a directory")) {
    // fix path / abort job
  } else throw e;
}

Prevention

When it happens

Trigger: Calling CompressionUtils.zip(File, OutputStream) with a path that points to a single file instead of a directory, or with a path that no longer exists (deleted between listing and zipping, or a wrong configured path).

Common situations: Configured task/restore directory paths pointing at files; symlinks resolved to files; jobs archiving segment directories after they were replaced by files in newer layouts.

Related errors


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

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/utils/CompressionUtils.java:265

  public static long zip(File directory, File outputZipFile) throws IOException
  {
    return zip(directory, outputZipFile, false);
  }

  /**
   * Zips the contents of the input directory to the output stream. Sub directories are skipped
   *
   * @param directory The directory whose contents should be added to the zip in the output stream.
   * @param out       The output stream to write the zip data to. Caller is responsible for closing this stream.
   *
   * @return The number of bytes (uncompressed) read from the input directory.
   *
   * @throws IOException
   */
  public static long zip(File directory, OutputStream out) throws IOException
  {
    if (!directory.isDirectory()) {
      throw new IOE("directory[%s] is not a directory", directory);
    }

    final ZipOutputStream zipOut = new ZipOutputStream(out);

    long totalSize = 0;

    // Sort entries to make life easier when writing streaming-decompression unit tests.
    for (File file : Arrays.stream(directory.listFiles()).sorted().collect(Collectors.toList())) {
      log.debug("Adding file[%s] with size[%,d].  Total size so far[%,d]", file, file.length(), totalSize);
      if (file.length() > Integer.MAX_VALUE) {
        zipOut.finish();
        throw new IOE("file[%s] too large [%,d]", file, file.length());
      }
      zipOut.putNextEntry(new ZipEntry(file.getName()));
      totalSize += Files.asByteSource(file).copyTo(zipOut);
    }
    zipOut.closeEntry();
    // Workaround for http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/759aa847dcaf

View on GitHub (pinned to 9b90983fd2)