{"record":{"id":"e3ac0b03f94fe0b0","repo":"apache/seatunnel","slug":"sftpexception-wrapped-symlink-resolution-failed","errorCode":null,"errorMessage":"SftpException wrapped (symlink resolution failed)","messagePattern":"SftpException wrapped \\(symlink resolution failed\\)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/system/SFTPFileSystem.java","lineNumber":262,"sourceCode":"\n    private FileStatus getFileStatus(\n            ChannelSftp channel, String fileName, SftpATTRS attr, Path parentPath)\n            throws IOException {\n        long length = attr.getSize();\n        boolean isDir = attr.isDir();\n        boolean isLink = attr.isLink();\n        if (isLink) {\n            String link = parentPath.toUri().getPath() + \"/\" + fileName;\n            try {\n                link = channel.realpath(link);\n\n                Path linkParent = new Path(\"/\", link);\n\n                FileStatus fstat = getFileStatus(channel, linkParent);\n                isDir = fstat.isDirectory();\n                length = fstat.getLen();\n            } catch (Exception e) {\n                throw new IOException(e);\n            }\n        }\n        int blockReplication = 1;\n        // Using default block size since there is no way in SFTP channel to know of\n        // block sizes on server. The assumption could be less than ideal.\n        long blockSize = DEFAULT_BLOCK_SIZE;\n        long modTime = attr.getMTime() * 1000L; // convert to milliseconds\n        long accessTime = attr.getATime() * 1000L;\n        FsPermission permission = new FsPermission((short) attr.getPermissions());\n        // not be able to get the real user group name, just use the user and group\n        // id\n        String user = Integer.toString(attr.getUId());\n        String group = Integer.toString(attr.getGId());\n        Path filePath = new Path(parentPath, fileName);\n\n        return new FileStatus(\n                length,\n                isDir,","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/apache/seatunnel/blob/cf67b549a7a6c35fa0beb12d83c62892427ea919/seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/system/SFTPFileSystem.java#L244-L280","documentation":"When the LsEntry indicates the path is a symbolic link, getFileStatus() recursively resolves the link target and computes isDir/length from it; any exception in that resolution (including SftpException from the recursive stat) is wrapped in a plain IOException. So this error means 'could not resolve a symlink to determine its status'.","triggerScenarios":"Stat'ing a path that is a symlink whose target is missing (dangling link), points outside the chroot, is cyclic, or whose parent is unreadable during the recursive getFileStatus call.","commonSituations":"Dangling symlinks left by previous jobs; links into directories the SFTP user cannot access; chroot setups where absolute link targets don't resolve; symlink loops.","solutions":["Remove or fix dangling/broken symlinks on the server (ls -l to identify them)","Ensure link targets are reachable from the SFTP user's chroot/permissions","Replace symlinks with real copies if the SFTP account cannot traverse targets","Check for symlink loops (link pointing to itself or an ancestor)","Inspect the wrapped cause to see the actual SftpException id"],"exampleFix":"// before\nFileStatus st = fs.getFileStatus(new Path(\"/data/latest\")); // symlink to deleted dir\n// after\n// on the server: rm /data/latest (dangling) and recreate the link or a real dir\nrm /data/latest\nln -s /data/current /data/latest","handlingStrategy":"validation","validationCode":"// server-side check before the job: find /data -type l ! -exec test -e {} \\; -print  # list dangling symlinks","typeGuard":null,"tryCatchPattern":"try { st = fs.getFileStatus(p); } catch (IOException e) { if (e.getCause() != null) { /* inspect symlink SftpException */ } throw e; }","preventionTips":["Run symlink audits (dangling/looping links) on the SFTP root","Ensure link targets are inside the chroot and readable","Replace symlinks with copies where the account cannot traverse","Log and inspect wrapped causes for SftpException ids","Avoid symlinked output paths in automated pipelines"],"tags":["sftp","symlink","file-status"],"backgroundTag":"broken-symlink","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}