apache/seatunnel · error · IOException
SFTP connection pool has been closed.
Error message
SFTP connection pool has been closed.
What it means
getFromPool throws this IOException when the pool's internal con2infoMap is null, which only happens after the pool has been shut down (close() nulls the map). Any request for a channel after shutdown is refused because the pool can no longer hand out or track connections.
Source
Thrown at seatunnel-connectors-v2/connector-file/connector-file-sftp/src/main/java/org/apache/seatunnel/connectors/seatunnel/file/sftp/system/SFTPConnectionPool.java:59
// have more live connections. It means that when we have more
// live connections than this threshold, any unused connection will be
// closed.
private int maxConnection;
private int liveConnectionCount;
private HashMap<ConnectionInfo, HashSet<ChannelSftp>> idleConnections =
new HashMap<ConnectionInfo, HashSet<ChannelSftp>>();
private HashMap<ChannelSftp, ConnectionInfo> con2infoMap =
new HashMap<ChannelSftp, ConnectionInfo>();
private HashMap<ChannelSftp, Session> con2sessionMap = new HashMap<ChannelSftp, Session>();
SFTPConnectionPool(int maxConnection, int liveConnectionCount) {
this.maxConnection = maxConnection;
this.liveConnectionCount = liveConnectionCount;
}
synchronized ChannelSftp getFromPool(ConnectionInfo info) throws IOException {
if (con2infoMap == null) {
throw new IOException("SFTP connection pool has been closed.");
}
Set<ChannelSftp> cons = idleConnections.get(info);
ChannelSftp channel;
if (cons != null && cons.size() > 0) {
Iterator<ChannelSftp> it = cons.iterator();
if (it.hasNext()) {
channel = it.next();
it.remove();
if (cons.isEmpty()) {
idleConnections.remove(info);
}
return channel;
} else {
throw new IOException("Connection pool error.");
}
}
return null;View on GitHub (pinned to cf67b549a7)
Solutions
- Do not use the filesystem/pool after close(); obtain a fresh SFTPFileSystem (and thus a new pool) for subsequent operations
- Guard concurrent close vs. use with proper lifecycle coordination — close only after all in-flight operations complete
- Check application code for double-close or stale cached filesystem instances
Example fix
// before pool.close(); ChannelSftp ch = pool.getFromPool(info); // throws // after ChannelSftp ch = pool.getFromPool(info); // ... use channel ... pool.close();
Defensive patterns
Strategy: try-catch
Validate before calling
if (!fs.isOpen() /* or track pool lifecycle yourself */) { recreate filesystem before use; } Try / catch
try {
ChannelSftp ch = pool.getFromPool(info);
} catch (IOException e) {
if (e.getMessage().contains("connection pool has been closed")) {
pool = newPool(); // recreate and retry once
} else throw e;
} Prevention
- Close the pool only after all tasks using it have completed
- Never cache SFTPFileSystem across job lifecycles
- Use reference counting or try-with-resources style ownership for the pool
When it happens
Trigger: Calling getFromPool (directly or via channel()/borrowedChannel() on SFTPFileSystem) after SFTPConnectionPool.close()/shutdown has run — e.g. using a cached SFTPFileSystem whose pool was closed, or concurrent use during filesystem close.
Common situations: A thread still performing file reads/writes while another thread closes the filesystem; reusing a FileSystem object across job/attempt lifecycles after cleanup; retry logic re-invoking the pool after a shutdown path.
Related errors
- Connection pool error.
- DataTypeChanger not reset
- Handler not reset
- MDCContext is not activated
- No split assigned
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/25d8a9a6ed558924.
Report an issue: GitHub.