apache/hadoop · error · FileNotFoundException

Path {file} is a directory.

Error message

Path {file} is a directory.

What it means

FTPFileSystem.open() stats the target before issuing RETR and refuses to open a directory as a stream. It throws FileNotFoundException even though the path exists — FTPFileSystem has no directory-stream semantics, and the message distinguishes this from a genuinely missing file.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/ftp/FTPFileSystem.java:284

   * @param path
   * @return
   */
  private Path makeAbsolute(Path workDir, Path path) {
    if (path.isAbsolute()) {
      return path;
    }
    return new Path(workDir, path);
  }

  @Override
  public FSDataInputStream open(Path file, int bufferSize) throws IOException {
    FTPClient client = connect();
    Path workDir = new Path(client.printWorkingDirectory());
    Path absolute = makeAbsolute(workDir, file);
    FileStatus fileStat = getFileStatus(client, absolute);
    if (fileStat.isDirectory()) {
      disconnect(client);
      throw new FileNotFoundException("Path " + file + " is a directory.");
    }
    client.allocate(bufferSize);
    Path parent = absolute.getParent();
    // Change to parent directory on the
    // server. Only then can we read the
    // file
    // on the server by opening up an InputStream. As a side effect the working
    // directory on the server is changed to the parent directory of the file.
    // The FTP client connection is closed when close() is called on the
    // FSDataInputStream.
    client.changeWorkingDirectory(parent.toUri().getPath());
    InputStream is = client.retrieveFileStream(file.getName());
    FSDataInputStream fis = new FSDataInputStream(new FTPInputStream(is,
        client, statistics));
    if (!FTPReply.isPositivePreliminary(client.getReplyCode())) {
      // The ftpClient is an inconsistent state. Must close the stream
      // which in turn will logout and disconnect from FTP server
      fis.close();

View on GitHub (pinned to 2add963021)

Solutions

  1. Check fs.getFileStatus(path).isDirectory() before open() and listStatus() or fail with a clear message
  2. Validate user-configured input paths at job setup time
  3. In the catch handler, re-stat the path to distinguish 'is a directory' from 'missing' before reporting

Example fix

// before
FSDataInputStream in = fs.open(path); // path is a dir -> FileNotFoundException

// after
FileStatus st = fs.getFileStatus(path);
if (st.isDirectory()) {
  throw new IllegalArgumentException("Input is a directory: " + path);
}
FSDataInputStream in = fs.open(path);
Defensive patterns

Strategy: validation

Validate before calling

FileStatus st = fs.getFileStatus(path);
if (st.isDirectory()) {
  throw new IllegalArgumentException("Expected a file but got directory: " + path);
}
FSDataInputStream in = fs.open(path);

Type guard

static boolean isRegularFile(FileSystem fs, Path p) throws IOException {
  FileStatus st = fs.getFileStatus(p);
  return st != null && st.isFile();
}

Try / catch

try {
  in = fs.open(path);
} catch (FileNotFoundException e) {
  // re-stat to distinguish 'is a directory' from truly missing
  if (fs.exists(path) && fs.getFileStatus(path).isDirectory()) {
    // handle wrong-type path
  }
}

Prevention

When it happens

Trigger: fs.open(path) where path is a directory on the FTP server: user-supplied or glob-resolved input path pointing at a directory; a file path reused as a directory by another writer.

Common situations: Input path configuration pointing at a directory instead of a file; code that assumes FileNotFoundException can only mean 'missing file' and mis-handles 'is a directory'; tools that pass through whatever path the user typed.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/155e2fcc2a10e7b4. Report an issue: GitHub.