Skip to content

[Bug]: Slack DMs misclassified as channels when channel_type contradicts D-prefix ID #25460

@mcaxtr

Description

@mcaxtr

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

  1. Configure OpenClaw with session.dmScope: "main" and channels.slack.dm.policy: "open"
  2. Send a direct message to the bot from Slack
  3. Check the session key via openclaw sessions list
  4. Observe the session key is agent:main:slack:channel:d0abc123 instead of agent:main:main

Expected behavior

Per documentation: "DMs share the main session (like WhatsApp/Telegram)"

  • DMs with channel IDs starting with D should be classified as kind: "direct"
  • DMs should route to agent:main:main when dmScope: "main" is configured
  • Outbound replies should auto-deliver to the DM channel

Actual behavior

Slack DMs are:

  • Classified as kind: "group" or kind: "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 message tool 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" type
  • C* → Public channel - always "channel" type
  • G* → 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions