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:
app_mention not sent — Slack does not fire app_mention events for bot-originated messages, so isMention is never pre-set by the adapter.
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.
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.0 — parseSlackMessage (line ~2441): const rawText = event.text || ""
@chat-adapter/slack@4.27.0 — handleMessageEvent (line ~1960): const isMention = event.type === "app_mention"
chat@4.27.0 — detectMention (line ~3527): only tests message.text against regex patterns
@chat-adapter/slack@4.27.0 — extractLinks (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.
Junior's mention detection relies entirely on
event.text, but bot messages built with Block Kit may carry the @mention only inevent.blocksas a rich-textuserelement — never serialized intoevent.text. Since Slack also does not fireapp_mentionfor bot-to-bot mentions, the mention goes completely undetected.Root cause
The data flow has three independent gaps that combine into one failure:
app_mentionnot sent — Slack does not fireapp_mentionevents for bot-originated messages, soisMentionis never pre-set by the adapter.parseSlackMessageignores blocks — Message text is built exclusively fromevent.text || "". Whenblocksis the canonical content andevent.textis a fallback string (or empty), mentions in rich-text user elements are lost.detectMentionchecks onlymessage.text— The chat SDK's fallback mention detector scansmessage.textfor@botUserIdpatterns but never inspectsmessage.raw.blocks.Relevant code paths:
@chat-adapter/slack@4.27.0—parseSlackMessage(line ~2441):const rawText = event.text || ""@chat-adapter/slack@4.27.0—handleMessageEvent(line ~1960):const isMention = event.type === "app_mention"chat@4.27.0—detectMention(line ~3527): only testsmessage.textagainst regex patterns@chat-adapter/slack@4.27.0—extractLinks(line ~2358): parsesevent.blocksbut only extractslinkelements, notuserelementsSlack 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.texthas no<@U_JUNIOR>, blocks do, noapp_mentionfires → mention missed entirely.Suggested fix
Add a block-level mention scan. Recommended approach:
This could live in the adapter's
parseSlackMessage(settingisMentionon the Message), in Junior'sprocessMessageoverride, or as a supplemental check alongsidedetectMention.Caveats
rich_textblocks (section, context) may contain mrkdwn text with<@USER_ID>— those should also be scanned.skipSelfMentiondoesn'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.textvsevent.blocksto confirm the gap.Reported by Markus Unterwaditzer. Action taken on behalf of David Cramer.