Bug Description
The SSH client manager can close a replacement connection when an old client emits a delayed event. Cleanup selects the current registration by the SSH configuration hash. A replacement client uses the same hash when its connection settings match.
The manager also retains the old client-to-hash mapping after cleanup. A later usage update from that old client can update the replacement client's idle timestamp.
A synchronous exception from Client.connect() can leave a rejected connection promise in the cache. Later calls with the same settings reuse the rejection instead of attempting a new connection.
Steps to Reproduce
Use a local unit test with mocked SSH transport. No external SSH server is required.
- Obtain client A from SSHClientsManager.getClient().
- Emit end on A to remove its registration.
- Obtain client B with the same connection settings.
- Emit close on A.
- Observe that the manager calls end on B and removes B from its cache.
The same sequence also fails with a delayed end or error event from A.
For idle tracking, call updateLastUsed(A) after B is registered. Observe that B's idle time changes.
For connection failure, make connect() throw synchronously. Call getClient() twice with the same settings. Observe that connect() runs only once because the rejected promise remains cached.
Expected Behavior
An event from A must only clean up A's registration. It must not close B or abort B's controller. Usage updates from a removed client must not change the replacement's idle time. A failed connection must be removed from the cache so a later call can attempt a new connection.
Debug Info
This is a source-level reproduction from n8n-io/n8n revision 909a6bc. It is not a report from a running n8n instance.
The expanded SSH unit suite contains 23 cases. With the original production file, 5 cases fail and 18 pass. With the proposed cleanup change, all 23 pass.
Affected source: packages/core/src/execution-engine/ssh-clients-manager.ts.
Operating System
macOS.
n8n Version
2.37.0 (source checkout).
Node.js Version
24.1.0. The test environment uses pnpm 11.25.0 and Vitest 4.1.9.
Database
Not applicable. The isolated reproduction uses mocked SSH transport and does not connect to a database.
Execution Mode
Not applicable. The reproduction runs the unit suite, not an n8n execution instance.
Hosting
Local source checkout. No cloud or self-hosted deployment was used.
Related Context
PR #16054 changed earlier SSH cleanup and idle tracking behavior. This report describes separate lifecycle cases. No claim is made that it resolves another open issue.
Bug Description
The SSH client manager can close a replacement connection when an old client emits a delayed event. Cleanup selects the current registration by the SSH configuration hash. A replacement client uses the same hash when its connection settings match.
The manager also retains the old client-to-hash mapping after cleanup. A later usage update from that old client can update the replacement client's idle timestamp.
A synchronous exception from Client.connect() can leave a rejected connection promise in the cache. Later calls with the same settings reuse the rejection instead of attempting a new connection.
Steps to Reproduce
Use a local unit test with mocked SSH transport. No external SSH server is required.
The same sequence also fails with a delayed end or error event from A.
For idle tracking, call updateLastUsed(A) after B is registered. Observe that B's idle time changes.
For connection failure, make connect() throw synchronously. Call getClient() twice with the same settings. Observe that connect() runs only once because the rejected promise remains cached.
Expected Behavior
An event from A must only clean up A's registration. It must not close B or abort B's controller. Usage updates from a removed client must not change the replacement's idle time. A failed connection must be removed from the cache so a later call can attempt a new connection.
Debug Info
This is a source-level reproduction from n8n-io/n8n revision 909a6bc. It is not a report from a running n8n instance.
The expanded SSH unit suite contains 23 cases. With the original production file, 5 cases fail and 18 pass. With the proposed cleanup change, all 23 pass.
Affected source: packages/core/src/execution-engine/ssh-clients-manager.ts.
Operating System
macOS.
n8n Version
2.37.0 (source checkout).
Node.js Version
24.1.0. The test environment uses pnpm 11.25.0 and Vitest 4.1.9.
Database
Not applicable. The isolated reproduction uses mocked SSH transport and does not connect to a database.
Execution Mode
Not applicable. The reproduction runs the unit suite, not an n8n execution instance.
Hosting
Local source checkout. No cloud or self-hosted deployment was used.
Related Context
PR #16054 changed earlier SSH cleanup and idle tracking behavior. This report describes separate lifecycle cases. No claim is made that it resolves another open issue.