Summary
The Slack Socket Mode adapter (crates/openfang-channels/src/slack.rs) does not deduplicate incoming events by envelope_id. Slack intentionally delivers the same event to multiple active WebSocket connections for reliability and expects apps to deduplicate. This results in the agent processing the same user message twice, sending duplicate replies to the channel.
Slack docs reference
From the official Slack Socket Mode guidance:
"Your app must implement deduplication based on envelope_id to avoid double-processing during connection rotation."
Socket Mode regularly rotates connections (disconnect: warning ~ every hour) and, during the grace window, an event can flow through both the old and the new connection.
Reproduction
- Configure a Slack app with Socket Mode + an OpenFang Slack channel.
- Send a message that mentions the bot.
- Trigger a connection rotation, either by waiting for Slack's periodic
disconnect: warning, or by restarting openfang.service mid-conversation.
- Observe the bot posting the same response twice.
Relevant server logs (redacted):
Apr 22 12:10:38 INFO Slack disconnect request: warning
Apr 22 12:10:38 WARN Slack: reconnecting in 1s
Apr 22 12:10:39 INFO Connecting to Slack Socket Mode...
Apr 22 12:10:40 INFO Slack Socket Mode connected
Apr 22 12:10:40 INFO Slack disconnect request: too_many_websockets
Apr 22 12:10:40 WARN Slack: reconnecting in 1s
Apr 22 12:10:41 INFO Connecting to Slack Socket Mode...
Apr 22 12:10:41 INFO Slack Socket Mode connected
Apr 22 12:10:41 INFO Slack disconnect request: too_many_websockets
...
In the user-facing Slack channel, the same @bot find emails: ... prompt produced two identical responses posted back-to-back.
Root cause
In crates/openfang-channels/src/slack.rs around lines 275–309, the events_api branch:
- Reads
envelope_id from payload
- Acknowledges the envelope
- Forwards the event to the internal
mpsc channel for agent dispatch
There is no check that envelope_id has been processed already. Grep confirms no other occurrence of envelope_id outside this ack path, and no dedup cache exists anywhere in the Slack adapter.
Proposed fix
Introduce a bounded TTL cache keyed by envelope_id and short-circuit before forwarding:
use dashmap::DashMap;
use std::time::{Duration, Instant};
use once_cell::sync::Lazy;
// 60-second TTL is well above the typical rotation overlap window (< 10s)
static RECENT_ENVELOPES: Lazy<DashMap<String, Instant>> = Lazy::new(DashMap::new);
const ENVELOPE_TTL: Duration = Duration::from_secs(60);
// ...inside the "events_api" branch, right after ack:
if !envelope_id.is_empty() {
// Opportunistic GC of expired entries (cap growth)
if RECENT_ENVELOPES.len() > 10_000 {
let now = Instant::now();
RECENT_ENVELOPES.retain(|_, ts| now.duration_since(*ts) < ENVELOPE_TTL);
}
if let Some(prev) = RECENT_ENVELOPES.get(envelope_id) {
if prev.elapsed() < ENVELOPE_TTL {
debug!("Slack: skipping duplicate envelope_id {envelope_id}");
continue;
}
}
RECENT_ENVELOPES.insert(envelope_id.to_string(), Instant::now());
}
The ack must still be sent (so Slack does not retry), but the event should not be forwarded to the mpsc channel if already seen.
Impact
Any OpenFang deployment using Slack Socket Mode with active usage periodically produces duplicate bot responses. It also amplifies any LLM/API cost and rate-limit pressure (each duplicate causes a full agent run + tool call).
Environment
- OpenFang:
main branch (reproduced on build from 2026-04-21)
- Slack Socket Mode:
xapp-* app-level token
- Adapter:
crates/openfang-channels/src/slack.rs
Happy to submit a PR with the dedup patch if desired.
Summary
The Slack Socket Mode adapter (
crates/openfang-channels/src/slack.rs) does not deduplicate incoming events byenvelope_id. Slack intentionally delivers the same event to multiple active WebSocket connections for reliability and expects apps to deduplicate. This results in the agent processing the same user message twice, sending duplicate replies to the channel.Slack docs reference
From the official Slack Socket Mode guidance:
Socket Mode regularly rotates connections (
disconnect: warning~ every hour) and, during the grace window, an event can flow through both the old and the new connection.Reproduction
disconnect: warning, or by restartingopenfang.servicemid-conversation.Relevant server logs (redacted):
In the user-facing Slack channel, the same
@bot find emails: ...prompt produced two identical responses posted back-to-back.Root cause
In
crates/openfang-channels/src/slack.rsaround lines 275–309, theevents_apibranch:envelope_idfrom payloadmpscchannel for agent dispatchThere is no check that
envelope_idhas been processed already. Grep confirms no other occurrence ofenvelope_idoutside this ack path, and no dedup cache exists anywhere in the Slack adapter.Proposed fix
Introduce a bounded TTL cache keyed by
envelope_idand short-circuit before forwarding:The ack must still be sent (so Slack does not retry), but the event should not be forwarded to the mpsc channel if already seen.
Impact
Any OpenFang deployment using Slack Socket Mode with active usage periodically produces duplicate bot responses. It also amplifies any LLM/API cost and rate-limit pressure (each duplicate causes a full agent run + tool call).
Environment
mainbranch (reproduced on build from 2026-04-21)xapp-*app-level tokencrates/openfang-channels/src/slack.rsHappy to submit a PR with the dedup patch if desired.