Skip to content

Bot mentions in blocks go undetected #358

@sentry-junior

Description

@sentry-junior

Junior's mention detection relies entirely on event.text, but bot messages built with Block Kit may carry the @mention only in event.blocks as a rich-text user element — never serialized into event.text. Since Slack also does not fire app_mention for bot-to-bot mentions, the mention goes completely undetected.

Root cause

The data flow has three independent gaps that combine into one failure:

  1. app_mention not sent — Slack does not fire app_mention events for bot-originated messages, so isMention is never pre-set by the adapter.
  2. parseSlackMessage ignores blocks — Message text is built exclusively from event.text || "". When blocks is the canonical content and event.text is a fallback string (or empty), mentions in rich-text user elements are lost.
  3. detectMention checks only message.text — The chat SDK's fallback mention detector scans message.text for @botUserId patterns but never inspects message.raw.blocks.

Relevant code paths:

  • @chat-adapter/slack@4.27.0parseSlackMessage (line ~2441): const rawText = event.text || ""
  • @chat-adapter/slack@4.27.0handleMessageEvent (line ~1960): const isMention = event.type === "app_mention"
  • chat@4.27.0detectMention (line ~3527): only tests message.text against regex patterns
  • @chat-adapter/slack@4.27.0extractLinks (line ~2358): parses event.blocks but only extracts link elements, not user elements

Slack payload shape for the failure case

{
  "type": "message",
  "subtype": "bot_message",
  "text": "some fallback text without mention",
  "bot_id": "B_OTHER_BOT",
  "blocks": [{
    "type": "rich_text",
    "elements": [{
      "type": "rich_text_section",
      "elements": [
        { "type": "user", "user_id": "U_JUNIOR" },
        { "type": "text", "text": " please look at this" }
      ]
    }]
  }]
}

In this case: event.text has no <@U_JUNIOR>, blocks do, no app_mention fires → mention missed entirely.

Suggested fix

Add a block-level mention scan. Recommended approach:

function slackBlocksMentionUser(event: Record<string, unknown>, botUserId: string): boolean {
  if (!botUserId || !event?.blocks || !Array.isArray(event.blocks)) return false;

  const visit = (node: unknown): boolean => {
    if (!node || typeof node !== "object") return false;
    const n = node as Record<string, unknown>;
    if (n.type === "user" && n.user_id === botUserId) return true;
    if (Array.isArray(n.elements)) return n.elements.some(visit);
    return false;
  };

  return (event.blocks as unknown[]).some(visit);
}

This could live in the adapter's parseSlackMessage (setting isMention on the Message), in Junior's processMessage override, or as a supplemental check alongside detectMention.

Caveats

  • Rich-text blocks can be nested (lists, quotes, preformatted). Use recursive traversal.
  • Non-rich_text blocks (section, context) may contain mrkdwn text with <@USER_ID> — those should also be scanned.
  • Verify skipSelfMention doesn't suppress the mention in edge cases where the bot is both sender and mentionee.

Verification

Gather a real Slack event payload from a bot that @mentions Junior with blocks, and log event.text vs event.blocks to confirm the gap.

Reported by Markus Unterwaditzer. Action taken on behalf of David Cramer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions