Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
Back to tool/Back to issues
#40113·n8n

Postgres node over SSH tunnel hangs indefinitely after the shared SSH client is closed

Author: lghartmannCreated Oct 1, 2026Updated Oct 1, 2026
Labelsteam:nodesstatus:in-linearstatus:team-assigned

Bug Description

Postgres nodes using an SSH-tunnelled credential can hang forever. They never error and never retry, and retryOnFail / onError: continueErrorOutput don't fire. Only the workflow timeout or a manual cancel stops them. While it's hanging, nothing reaches the SSH server or the database.

It happens when two Postgres nodes with different typeVersions (e.g. 2.5 and 2.6) use the same SSH credential:

  • ConnectionPoolManager keys pools by node version, so they get two pools.
  • SSHClientsManager keys SSH clients by host/user/key only, so both pools share one SSH client.

Only the pool that created the SSH client is subscribed to its lifecycle (the existing branch in SSHClientsManager.getClient ignores the caller's abortController).

When that pool is evicted after its 5-min TTL, its abort closes the shared SSH client while the other pool is still using it. The surviving pool keeps the closed client. The next query connects to the local TCP proxy, and sshClient.forwardOut() throws Error: Not connected synchronously (ssh2 1.15.0, lib/client.js:1401) inside the proxy's connection listener. That throw ends up in the global uncaughtException handler. The accepted socket is never piped or destroyed, so pg waits on it forever.

Two related details:

  • The node's connectionTimeout option doesn't help, because it isn't part of the pool cache key. A pool keeps the timeout of whichever node created it.
  • The forwardOut error-callback path has the same socket leak: it closes the proxy server but not the socket it already accepted.

To Reproduce

  1. Create a Postgres credential with SSH Tunnel enabled.
  2. Workflow A: Postgres node typeVersion 2.5, Execute Query SELECT 1. Workflow B: Postgres node typeVersion 2.6, SELECT 1.
  3. Run A once, then B within a few seconds. B reuses A's SSH client, and the SSH server shows only one session.
  4. Keep running B every ~30s. Don't run A again.
  5. About 5 min after A's last run, A's pool is evicted and the SSH session closes. Every following run of B hangs, and Error: Not connected is reported as an uncaught exception.

We reproduced this on n8nio/n8n:2.41.4 against a local postgres:16 and an OpenSSH server, using n8n's compiled SSHClientsManager and configurePostgres. Excerpt:

[t+272.5s] B (v2.6): ok (174ms)
[t+300.3s] ConnectionPoolManager: Found stale pool. Cleaning it up.
[t+300.3s] Got abort signal, cleaning up ssh client.
[t+302.5s] uncaughtException: Not connected
[t+322.6s] B (v2.6, connectionTimeout 10): HUNG (no result after 20s)
... every later query: "Not connected" + hang

### Expected behavior

If the tunnel is gone, the query should fail promptly with an error, so retries and error outputs work. The next execution should open a fresh SSH connection.


### Debug Info

- n8n version: 2.41.4 (n8n Cloud)
- ssh2: 1.15.0
- Database: PostgreSQL 16 (AWS RDS) behind an OpenSSH bastion

### Operating System

n8n cloud

### n8n Version

2.41.4

### Node.js Version

26.7.0

### Database

PostgreSQL

### Execution mode

main (default)

### Hosting

n8n cloud

Source: n8n-io/n8n

View original on GitHubView discussion on GitHub