{"record":{"id":"e22a946aa052320b","repo":"apache/seatunnel","slug":"connection-pool-error","errorCode":null,"errorMessage":"Connection pool error.","messagePattern":"Connection pool error\\.","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/SFTPConnectionPool.java","lineNumber":74,"sourceCode":"\n    synchronized ChannelSftp getFromPool(ConnectionInfo info) throws IOException {\n        if (con2infoMap == null) {\n            throw new IOException(\"SFTP connection pool has been closed.\");\n        }\n        Set<ChannelSftp> cons = idleConnections.get(info);\n        ChannelSftp channel;\n\n        if (cons != null && cons.size() > 0) {\n            Iterator<ChannelSftp> it = cons.iterator();\n            if (it.hasNext()) {\n                channel = it.next();\n                it.remove();\n                if (cons.isEmpty()) {\n                    idleConnections.remove(info);\n                }\n                return channel;\n            } else {\n                throw new IOException(\"Connection pool error.\");\n            }\n        }\n        return null;\n    }\n\n    synchronized boolean returnToPool(ChannelSftp channel) {\n        if (con2infoMap == null) {\n            return false;\n        }\n        ConnectionInfo info = con2infoMap.get(channel);\n        if (info == null) {\n            return false;\n        }\n        HashSet<ChannelSftp> cons = idleConnections.get(info);\n        if (cons == null) {\n            cons = new HashSet<ChannelSftp>();\n            idleConnections.put(info, cons);\n        }","sourceCodeStart":56,"sourceCodeEnd":92,"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/SFTPConnectionPool.java#L56-L92","documentation":"getFromPool throws this IOException when the pool finds an idleConnections entry whose Set is empty (size 0) but non-null — an internal inconsistency, since idle channels should have been removed together with their empty set. It signals corrupted pool bookkeeping.","triggerScenarios":"An idleConnections map entry exists for the ConnectionInfo key but its channel set is empty when getFromPool iterates it; the else branch fires and throws. This is effectively an internal invariant violation inside SFTPConnectionPool.","commonSituations":"Rare; typically seen under concurrent mutation of pool state if synchronization assumptions are broken, or after abnormal channel return/removal sequences leaving an empty set registered.","solutions":["This indicates a library-internal bug: report it / upgrade the connector version where pool bookkeeping is fixed","Recycle the SFTPFileSystem/pool: close it and create a new one to clear corrupted state","Retry the operation with a fresh filesystem instance"],"exampleFix":"// before\nSFTPFileSystem fs = cachedFs; // pool state corrupted\n// after\ncachedFs.close();\nSFTPFileSystem fs = (SFTPFileSystem) FileSystem.get(uri, conf); // fresh pool","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    pool.getFromPool(info);\n} catch (IOException e) {\n    if (\"Connection pool error.\".equals(e.getMessage())) {\n        recreateFileSystem(); // pool bookkeeping corrupted; start fresh\n    } else throw e;\n}","preventionTips":["Report persistent occurrences as a library bug","Avoid custom reflection into pool internals; use only public API","Recycle the filesystem on any inexplicable pool error"],"tags":["sftp","connection-pool","internal-state"],"backgroundTag":"internal-invariant-violation","analyzedSha":"cf67b549a7a6c35fa0beb12d83c62892427ea919","analyzedAt":"2026-09-10T21:44:55.265Z","contentChangedAt":"2026-09-10T21:44:55.265Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}