apache/seatunnel · error · IOException

Seek not supported

Error message

Seek not supported

What it means

SFTPInputStream.seek() is not implemented: this stream is a sequential SFTP read and cannot jump to an arbitrary position. Any seek call throws IOException with 'Seek not supported'. seekToNewSource behaves the same way.

Solutions

  1. Re-open the SFTPInputStream and skip(pos) to the desired offset instead of seeking.
  2. Wrap or choose a source/stream implementation that supports seek if random access is required.
  3. Restructure the consumer to read sequentially without repositioning (buffer data if needed).
  4. If using a framework with a 'seekable/seek' option, disable it for SFTP sources.

Example fix

// before
in.seek(checkpointOffset); // throws
// after
in.close();
in = SFTPInputStream.getInstance(path, ...);
IOUtils.skipFully(in, checkpointOffset);
Defensive patterns

Strategy: fallback

Validate before calling

// No validation possible; SFTPInputStream never supports seek — avoid seekable consumers

Type guard

boolean supportsSeek(InputStream in) { return !(in instanceof SFTPInputStream); }

Try / catch

try { in.seek(pos); }
catch (IOException e) {
    if ("Seek not supported".equals(e.getMessage())) { reopenAndSkip(in, pos); }
    else throw e;
}

Prevention

When it happens

Trigger: Calling seek(pos) on an SFTPInputStream, typically when a framework (e.g. a reader doing split re-processing, checkpoint restore, or random-access reads) assumes a seekable stream.

Common situations: Recovering a failed read by seeking back to a saved offset; using a reader that requires Seekable input against an SFTP source; framework retry logic that repositions streams mid-file.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/a87d52f0026dc5ff. 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/SFTPInputStream.java:67

        if (stream == null) {
            throw new IllegalArgumentException(E_NULL_INPUT_STREAM);
        }
        if (channel == null || !channel.isConnected()) {
            throw new IllegalArgumentException(E_CLIENT_NULL);
        }
        this.wrappedStream = stream;
        this.channel = channel;
        this.connectionPool = connectionPool;
        this.stats = stats;

        this.pos = 0;
        this.closed = false;
    }

    @Override
    public void seek(long position) throws IOException {
        throw new IOException(E_SEEK_NOT_SUPPORTED);
    }

    @Override
    public boolean seekToNewSource(long targetPos) throws IOException {
        throw new IOException(E_SEEK_NOT_SUPPORTED);
    }

    @Override
    public long getPos() throws IOException {
        return pos;
    }

    @Override
    public synchronized int read() throws IOException {
        if (closed) {
            throw new IOException(E_STREAM_CLOSED);
        }

View on GitHub (pinned to cf67b549a7)