-
-
Notifications
You must be signed in to change notification settings - Fork 52.6k
Description
Summary
Slack DMs (channel IDs starting with D) are misclassified as channels/groups when Slack's event payload provides a channel_type that contradicts the channel ID prefix. This causes DMs to bypass dmScope: "main" routing and create separate per-channel sessions instead of merging with the main session.
Steps to reproduce
- Configure OpenClaw with
session.dmScope: "main"andchannels.slack.dm.policy: "open" - Send a direct message to the bot from Slack
- Check the session key via
openclaw sessions list - Observe the session key is
agent:main:slack:channel:d0abc123instead ofagent:main:main
Expected behavior
Per documentation: "DMs share the main session (like WhatsApp/Telegram)"
- DMs with channel IDs starting with
Dshould be classified askind: "direct" - DMs should route to
agent:main:mainwhendmScope: "main"is configured - Outbound replies should auto-deliver to the DM channel
Actual behavior
Slack DMs are:
- Classified as
kind: "group"orkind: "channel"(wrong) - Routed to
agent:main:slack:channel:d0acp6b1t8v(bypasses dmScope) - Not merging with main session
- Outbound auto-replies don't deliver (wrong session context)
OpenClaw version
2026.2.24 (current main branch, commit 2bad30b)
Operating system
All (macOS, Linux, Windows)
Install method
npm global / mac app / docker (affects all install methods)
Logs, screenshots, and evidence
Current code in src/slack/monitor/context.ts:36-50:
export function normalizeSlackChannelType(
channelType?: string | null,
channelId?: string | null,
): SlackMessageEvent["channel_type"] {
const normalized = channelType?.trim().toLowerCase();
if (
normalized === "im" ||
normalized === "mpim" ||
normalized === "channel" ||
normalized === "group"
) {
return normalized; // ❌ Trusts channel_type without validation
}
return inferSlackChannelType(channelId) ?? "channel";
}Problem: When Slack sends a DM (channel ID D0ABC123) with channel_type: "channel", the function returns "channel" instead of "im".
Example session state (from sessions_list):
{
"key": "agent:main:slack:channel:d0acp6b1t8v",
"kind": "group",
"channel": "slack",
"displayName": "slack:g-d0acp6b1t8v"
}Note: Channel ID D0ACP6B1T8V has the D prefix which definitively indicates a Slack DM per Slack's documented ID schema.
Impact and severity
Affected: Users who configure session.dmScope: "main" expecting unified DM sessions
Severity: High (breaks core DM routing functionality, silent data fragmentation)
Frequency: Occurs when Slack API sends contradicting channel_type metadata (intermittent but reproducible)
Consequence:
- DM conversations are fragmented across separate sessions instead of unified in main
- DM policy checks are bypassed (messages treated as channel messages)
- Outbound replies don't auto-deliver because they're routed to wrong session
- Users must manually use
messagetool to send replies - Conversation history is lost/isolated per-channel instead of unified
- Debugging is difficult because session routing silently ignores dmScope config
Additional information
Root cause: normalizeSlackChannelType() trusts the event's channel_type field even when it contradicts the channel ID prefix. Slack channel IDs starting with D are always DMs by Slack's own documented convention, regardless of what channel_type says.
Slack channel ID schema (canonical):
D*→ Direct message (DM) - always"im"typeC*→ Public channel - always"channel"typeG*→ Ambiguous (can be private channel OR group DM - trust provided type)
Workaround: Manually send messages using explicit message tool calls instead of relying on session routing.
This was previously reported in #8421 (closed as stale, not fixed).