{"record":{"id":"bb2fa369fe666b73","repo":"apache/seatunnel","slug":"path-file-is-a-directory","errorCode":null,"errorMessage":"Path ${file} is a directory.","messagePattern":"Path (.+?) is a directory\\.","errorType":"exception","errorClass":"FileNotFoundException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-file/connector-file-ftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/ftp/system/SeaTunnelFTPFileSystem.java","lineNumber":310,"sourceCode":"            }\n            if (filePath.startsWith(workDir.toUri().getPath())) {\n                return path;\n            }\n            // delete '/'\n            return new Path(workDir, filePath.substring(1));\n        }\n        return new Path(workDir, path);\n    }\n\n    @Override\n    public FSDataInputStream open(Path file, int bufferSize) throws IOException {\n        FTPClient client = connect();\n        Path workDir = new Path(client.printWorkingDirectory());\n        Path absolute = makeAbsolute(workDir, file);\n        FileStatus fileStat = getFileStatus(client, absolute);\n        if (fileStat.isDirectory()) {\n            disconnect(client);\n            throw new FileNotFoundException(\"Path \" + file + \" is a directory.\");\n        }\n        client.allocate(bufferSize);\n        Path parent = absolute.getParent();\n        // Change to parent directory on the\n        // server. Only then can we read the\n        // file\n        // on the server by opening up an InputStream. As a side effect the working\n        // directory on the server is changed to the parent directory of the file.\n        // The FTP client connection is closed when close() is called on the\n        // FSDataInputStream.\n        client.changeWorkingDirectory(parent.toUri().getPath());\n        InputStream is = client.retrieveFileStream(file.getName());\n        FSDataInputStream fis = new FSDataInputStream(new FTPInputStream(is, client, statistics));\n        if (!FTPReply.isPositivePreliminary(client.getReplyCode())) {\n            // The ftpClient is an inconsistent state. Must close the stream\n            // which in turn will logout and disconnect from FTP server\n            fis.close();\n            throw new IOException(\"Unable to open file: \" + file + \", Aborting\");","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-file/connector-file-ftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/ftp/system/SeaTunnelFTPFileSystem.java#L292-L328","documentation":"SeaTunnelFTPFileSystem.open() first resolves the file status; if the target path is a directory it throws java.io.FileNotFoundException with 'Path <file> is a directory.' This guards the read path because directories cannot be opened as input streams.","triggerScenarios":"Calling open(file) where getFileStatus() reports the path as a directory — e.g. the configured path in the source points at a directory rather than a file, or a filename match pattern resolved to a directory.","commonSituations":"SeaTunnel file source configured with a directory path where a concrete file was expected; path constructed with a trailing slash; a rename/move left the expected file missing and a directory of the same name present.","solutions":["Point open()/the config at an actual file path, not a directory","List the parent directory to confirm the intended file exists","Remove the trailing slash or directory component from the path"],"exampleFix":"// before\nfs.open(new Path(\"ftp://host/data/reports\")) // a directory\n// after\nfs.open(new Path(\"ftp://host/data/reports/2024.csv\"))","handlingStrategy":"validation","validationCode":"// Java\nFileSystem fs = path.getFileSystem(conf);\nFileStatus st = fs.getFileStatus(path);\nif (st.isDirectory()) throw new IllegalArgumentException(\"Expected a file, got directory: \" + path);","typeGuard":null,"tryCatchPattern":"try {\n    return fs.open(path);\n} catch (FileNotFoundException e) {\n    if (e.getMessage() != null && e.getMessage().endsWith(\"is a directory.\")) {\n        throw new IllegalArgumentException(\"Point the source at a file, not a directory: \" + path, e);\n    }\n    throw e;\n}","preventionTips":["Resolve globs/directories into concrete file lists before opening","Strip trailing slashes from configured paths","Re-verify the target still exists as a file right before open (race with renames)"],"tags":["ftp","filesystem","file","path"],"backgroundTag":"path-is-not-a-file","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}