apache/seatunnel · error · IOException

SftpException wrapped (symlink resolution failed)

Error message

SftpException wrapped (symlink resolution failed)

What it means

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'.

Solutions

  1. Remove or fix dangling/broken symlinks on the server (ls -l to identify them)
  2. Ensure link targets are reachable from the SFTP user's chroot/permissions
  3. Replace symlinks with real copies if the SFTP account cannot traverse targets
  4. Check for symlink loops (link pointing to itself or an ancestor)
  5. Inspect the wrapped cause to see the actual SftpException id

Example fix

// before
FileStatus st = fs.getFileStatus(new Path("/data/latest")); // symlink to deleted dir
// after
// on the server: rm /data/latest (dangling) and recreate the link or a real dir
rm /data/latest
ln -s /data/current /data/latest
Defensive patterns

Strategy: validation

Validate before calling

// server-side check before the job: find /data -type l ! -exec test -e {} \; -print  # list dangling symlinks

Try / catch

try { st = fs.getFileStatus(p); } catch (IOException e) { if (e.getCause() != null) { /* inspect symlink SftpException */ } throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/e3ac0b03f94fe0b0. Report an issue: GitHub.

Appendix: source

Thrown at seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/system/SFTPFileSystem.java:262

    private FileStatus getFileStatus(
            ChannelSftp channel, String fileName, SftpATTRS attr, Path parentPath)
            throws IOException {
        long length = attr.getSize();
        boolean isDir = attr.isDir();
        boolean isLink = attr.isLink();
        if (isLink) {
            String link = parentPath.toUri().getPath() + "/" + fileName;
            try {
                link = channel.realpath(link);

                Path linkParent = new Path("/", link);

                FileStatus fstat = getFileStatus(channel, linkParent);
                isDir = fstat.isDirectory();
                length = fstat.getLen();
            } catch (Exception e) {
                throw new IOException(e);
            }
        }
        int blockReplication = 1;
        // Using default block size since there is no way in SFTP channel to know of
        // block sizes on server. The assumption could be less than ideal.
        long blockSize = DEFAULT_BLOCK_SIZE;
        long modTime = attr.getMTime() * 1000L; // convert to milliseconds
        long accessTime = attr.getATime() * 1000L;
        FsPermission permission = new FsPermission((short) attr.getPermissions());
        // not be able to get the real user group name, just use the user and group
        // id
        String user = Integer.toString(attr.getUId());
        String group = Integer.toString(attr.getGId());
        Path filePath = new Path(parentPath, fileName);

        return new FileStatus(
                length,
                isDir,

View on GitHub (pinned to cf67b549a7)