From 93102e91cfec4ec7226f5cb495e19be784be83dc Mon Sep 17 00:00:00 2001 From: shivasymbl Date: Thu, 4 Jun 2026 13:39:17 +0530 Subject: [PATCH] fix(slack): add catch-all event handler to prevent Slack auto-disabling Event Subscriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without a catch-all handler, slack-bolt returns HTTP 404 for every unhandled bot event (user_change, user_huddle_changed, reaction_added, etc.) and never sends the Socket Mode ack. On active Slack workspaces where the app is subscribed to high-volume events, this produces a near-100% un-acked failure rate that crosses Slack's >95%/60-min threshold and triggers automatic disabling of the app's Event Subscriptions — silently killing all inbound event delivery. Place a catch-all re.compile(r".*") handler AFTER the specific event handlers so bolt's router matches those first. Truly unhandled events are silently acked (200) and logged at DEBUG. The failure rate stays near 0% regardless of which events the Slack app manifest subscribes to. Fixes #6572 --- plugins/platforms/slack/adapter.py | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/plugins/platforms/slack/adapter.py b/plugins/platforms/slack/adapter.py index 73f7116dbe1e..58ba0d91f10d 100644 --- a/plugins/platforms/slack/adapter.py +++ b/plugins/platforms/slack/adapter.py @@ -1751,6 +1751,46 @@ class SlackAdapter(BasePlatformAdapter): async def handle_assistant_thread_context_changed(event, say, body): await self._handle_assistant_thread_lifecycle_event(event, body) + # Catch-all no-op ack for any other subscribed event type that + # Hermes has no listener for (e.g. user_change, + # user_huddle_changed, member_joined_channel, channel_archive, + # pin_added, etc.). + # + # Two reasons this must exist (issues #6572 and the Event + # Subscriptions auto-disable failure mode): + # 1. Correctness at scale: without a matching listener, + # slack-bolt returns HTTP 404 for every unhandled event + # envelope and never sends the Socket Mode ack. When the app + # is subscribed to high-volume events (user_change fires on + # every presence/status change for the whole org), the flood + # of un-acked 404s pushes Slack's failure rate past its + # 95%/60-min threshold and Slack auto-disables the app's + # Event Subscriptions — silently killing ALL inbound + # delivery until manually re-enabled. + # 2. Noise: each unhandled envelope also logs a slack_bolt + # "Unhandled request" WARNING, flooding gateway logs in + # busy channels. + # + # Registered AFTER every named handler: bolt dispatches to the + # first matching listener, so the named handlers above always + # win and this only fires for truly unhandled types. The + # envelope is acked with 200, keeping the failure rate near 0% + # regardless of which events the Slack app manifest subscribes + # to. A debug line preserves visibility into unknown event + # types without per-message WARNING noise. + @self._app.event(re.compile(r".*")) + async def handle_unhandled_event(event, body, logger): + logger.debug( + "[Slack] Ignoring unhandled event type=%s (no listener " + "registered; subscribed events not handled by Hermes can " + "be removed from the Slack app manifest via " + "`hermes slack manifest`)", + (event or {}).get( + "type", + (body or {}).get("event", {}).get("type", "unknown"), + ), + ) + # Register slash command handler(s) # # Every gateway command from COMMAND_REGISTRY is a native Slack