{"record":{"id":"0d5df886e41b870d","repo":"apache/druid","slug":"cannot-list-contents-of-s-since-it-is-not-a-dir","errorCode":null,"errorMessage":"Cannot list contents of [%s] since it is not a directory","messagePattern":"Cannot list contents of \\[(.+?)\\] since it is not a directory","errorType":"exception","errorClass":"org.apache.druid.java.util.common.IAE","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/storage/local/LocalFileStorageConnector.java","lineNumber":161,"sourceCode":"   * @param dirName path\n   * @throws IOException thrown in case of errors.\n   */\n  @Override\n  public void deleteRecursively(String dirName) throws IOException\n  {\n    log.debug(\"Deleting directory at path: [%s]\", dirName);\n    FileUtils.deleteDirectory(fileWithBasePath(dirName));\n  }\n\n  @Override\n  public Iterator<String> listDir(String dirName)\n  {\n    File directory = fileWithBasePath(dirName);\n    if (!directory.exists()) {\n      throw new IAE(\"No directory exists on path [%s]\", dirName);\n    }\n    if (!directory.isDirectory()) {\n      throw new IAE(\"Cannot list contents of [%s] since it is not a directory\", dirName);\n    }\n    File[] files = directory.listFiles();\n    if (files == null) {\n      throw new ISE(\"Unable to fetch the file list from the path [%s]\", dirName);\n    }\n    return Arrays.stream(files).map(File::getName).iterator();\n  }\n\n  public File getBasePath()\n  {\n    return basePath;\n  }\n\n  private File fileWithBasePath(String path)\n  {\n    return new File(basePath, path);\n  }\n","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/storage/local/LocalFileStorageConnector.java#L143-L179","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix the caller to pass a directory path, not a file path","Verify the path on disk with ls -la and correct the directory layout","Add an isDirectory check in the caller before invoking listDir"],"exampleFix":"// before\nconnector.listDir(\"taskDir/task-123/index.zip\");\n// after\nconnector.listDir(\"taskDir/task-123\");","handlingStrategy":"validation","validationCode":"File dir = new File(basePath, dirName);\nif (!dir.isDirectory()) {\n  throw new IllegalArgumentException(dirName + \" is not a directory\");\n}\nconnector.listDir(dirName);","typeGuard":"static boolean isDirectory(File f) { return f != null && f.isDirectory(); }","tryCatchPattern":"try { connector.listDir(dir); } catch (IllegalArgumentException e) { logger.error(e, \"not a directory: %s\", dir); }","preventionTips":["Validate the path with File.isDirectory() before calling listDir","Never pass full file paths (e.g. index.zip) to directory-listing APIs","Log the resolved absolute path when configuring directory roots"],"tags":["filesystem","illegal-argument"],"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"}