{"record":{"id":"afe662c28a6aa935","repo":"apache/druid","slug":"directory-s-is-not-a-directory","errorCode":null,"errorMessage":"directory[%s] is not a directory","messagePattern":"directory\\[(.+?)\\] is not a directory","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/utils/CompressionUtils.java","lineNumber":265,"sourceCode":"  public static long zip(File directory, File outputZipFile) throws IOException\n  {\n    return zip(directory, outputZipFile, false);\n  }\n\n  /**\n   * Zips the contents of the input directory to the output stream. Sub directories are skipped\n   *\n   * @param directory The directory whose contents should be added to the zip in the output stream.\n   * @param out       The output stream to write the zip data to. Caller is responsible for closing this stream.\n   *\n   * @return The number of bytes (uncompressed) read from the input directory.\n   *\n   * @throws IOException\n   */\n  public static long zip(File directory, OutputStream out) throws IOException\n  {\n    if (!directory.isDirectory()) {\n      throw new IOE(\"directory[%s] is not a directory\", directory);\n    }\n\n    final ZipOutputStream zipOut = new ZipOutputStream(out);\n\n    long totalSize = 0;\n\n    // Sort entries to make life easier when writing streaming-decompression unit tests.\n    for (File file : Arrays.stream(directory.listFiles()).sorted().collect(Collectors.toList())) {\n      log.debug(\"Adding file[%s] with size[%,d].  Total size so far[%,d]\", file, file.length(), totalSize);\n      if (file.length() > Integer.MAX_VALUE) {\n        zipOut.finish();\n        throw new IOE(\"file[%s] too large [%,d]\", file, file.length());\n      }\n      zipOut.putNextEntry(new ZipEntry(file.getName()));\n      totalSize += Files.asByteSource(file).copyTo(zipOut);\n    }\n    zipOut.closeEntry();\n    // Workaround for http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/759aa847dcaf","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/utils/CompressionUtils.java#L247-L283","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Verify the path is a directory before calling: file.isDirectory() (and exists())","Point zip() at the parent directory if the intent was to archive a single file — or use file-copy helpers for single files","Fix the configured directory path in job/spec config that resolved to a file","Create the directory if it is legitimately missing (FileUtils.mkdirhier) before zipping"],"exampleFix":"// before\nCompressionUtils.zip(new File(\"/data/segments/segment-file\"), out); // throws\n// after\nFile dir = new File(\"/data/segments/my-segment\");\nif (!dir.isDirectory()) {\n  throw new IllegalArgumentException(\"Expected a directory: \" + dir);\n}\nCompressionUtils.zip(dir, out);","handlingStrategy":"validation","validationCode":"File dir = new File(path);\nif (!dir.isDirectory()) {\n  throw new IllegalArgumentException(\"Not a directory: \" + path);\n}","typeGuard":null,"tryCatchPattern":"try {\n  CompressionUtils.zip(dir, out);\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"is not a directory\")) {\n    // fix path / abort job\n  } else throw e;\n}","preventionTips":["Call file.isDirectory() before zip()","Resolve symlinks before passing paths","Validate configured directory paths at job start"],"tags":["java","ioexception","compression","path-validation"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}