slackapi / slackapi/python-slack-sdk
Socket Mode: reconnects behind NAT leak server-side connection registrations → too_many_websockets cap → silent loss of interactive payloads; mitigation proposals
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 4k
- Forks
- 857
- Avg merge
- 22h 21m
- Merged PRs (30d)
- 16
Description
Slack SDK version: slack-sdk 3.43.0, slack-bolt 1.29.0
Python: 3.13 (aiohttp Socket Mode client)
OS/platform: Linux container (Docker) on macOS host, behind NAT
Summary
This is part bug report, part mitigation proposal, backed by wire-level data.
When SocketModeClient reconnects while the network path is degraded (half-open
TCP: the close frame never reaches Slack), the old connection remains registered
server-side for an extended period. Repeated reconnects therefore accumulate
"ghost" registrations up to Slack's 10-connection cap (disconnect: too_many_websockets). Slack then delivers envelopes across all registered
connections, so most interactive (block_actions) payloads — which unlike
events are not retried on non-ack — are silently lost. From the app's
perspective the client looks perfectly healthy: ping/pong fine, events flowing.
Wire evidence
(from a run_message_listeners wrapper logging hello and disconnect frames)
- fresh start:
helloreportsnum_connections=1 - ~1.5 h later, on a reconnect: 3x
disconnect reason=too_many_websockets,
thenhello num_connections=10— while the process verifiably held ONE
established TCP connection to Slack the whole time - ghost registrations age out at roughly one per 30-45 minutes
- while
num_connectionsis high, most button clicks never arrive on any
connection we hold; with a clean pool, every click arrives (tested across
message sizes 0.5-5 KB — size is irrelevant) - reproduced on a SECOND app in the same workspace: first
helloafter a
process restart reportednum_connections=7for an app that also runs as a
single instance - observed
approximate_connection_time(insidehello.debug_info) is
consistently18060(~5 h), which sets the ghost age-out horizon
Why this is hard to see with the current SDK
- The
helloenvelope (carryingnum_connections) never reaches
message_listeners—SocketModeRequest.from_dictrequires
type+envelope_id+payload, so apps cannot observe the most important signal
without wrapping internals. disconnectframes (includingtoo_many_websockets) are handled by
run_message_listenersbefore the listener loop and only visible at debug
logging.- A degraded connection still passes
is_connected()/ ping-pong checks, so
client-side health monitoring cannot detect the server-side pool state.
Proposals (any subset would help)
- Surface
hellometadata (num_connections,approximate_connection_time,
host) anddisconnectreasons via a public callback or at INFO logging. - Emit a loud warning when
num_connectionsinhelloexceeds a threshold
(e.g., 4) while the client manages fewer connections — this is direct
evidence of ghost registrations and imminent interactive-payload loss. - Consider make-before-break reconnects with close-confirmation, or documenting
that reconnect-heavy operation behind NAT can poison the server-side pool. - Still-open PRs #1914 / #1926 address orphaned client-side sessions in the
same failure family — this issue is their server-side counterpart, and since
neither is merged/released (latest release is 3.43.0), apps currently have no
upstream remedy at all; that raises the priority of surfacing the diagnostics
from proposals 1-2.
Happy to share full logs and reproduction notes. We have also filed a parallel
report with Slack developer support regarding the server-side routing/eviction
behavior; will cross-link.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing run_message_listeners and SocketModeRequest.from_dict, focusing on how hello metadata and disconnect reasons are handled before message listeners receive frames. Reproduce the reconnect behavior if possible, then define diagnostics that expose the server-side connection count and reasons clearly enough to warn about an unhealthy pool.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, networking
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100