Skip to content

bug(discord): streamed responses post empty MESSAGE_CREATE then MESSAGE_UPDATE patches — receiver bots in multi-bot threads silently miss them #990

Description

@brettchien

Description

When Discord streaming is enabled, openab posts a placeholder via MESSAGE_CREATE and patches the real content in through repeated MESSAGE_UPDATE (~1500ms interval, see adapter.rs:485-522). Receiver bots' EventHandler::message() fires on MESSAGE_CREATE only — they see the empty placeholder, never the mention/content patched in later. There is no message_update() handler in discord.rs (handler trait at src/discord.rs:323+).

Result: in multi-bot threads, a streaming sender's mention silently fails to trigger receiver bots. Per-thread multibot streaming gating (#534 / #535) is supposed to mitigate this, but has gaps:

  • Cold-start threads where multibot_threads cache hasn't seen the other bots yet
  • Sender's first response in a thread before detection populates the cache
  • Any path where use_streaming() returns true while ≥2 bots are actually present

Steps to Reproduce

  1. Create a Discord thread with three bots A, B, C, all running openab.
  2. Bot A sends a mention-bearing message @B @C <task> through normal streaming pipeline (no curl, no special config).
  3. Bot A's content arrives as MESSAGE_CREATE { content: "…" } then a sequence of MESSAGE_UPDATE events filling in <@B> <@C> <task>.
  4. Observe: B and C never dispatch — their brokers logged the MESSAGE_CREATE with empty/placeholder content and treated it as not-for-me; the subsequent MESSAGE_UPDATE events are ignored.

Expected Behavior

Two complementary fixes (defense in depth):

(a) Proactively disable streaming in any multi-bot-capable thread. Don't wait for multibot_threads cache to observe the other bots — make use_streaming() return false for any thread whose channel config allows bot-to-bot messages, or detect bot members at thread-creation time. Closes the cold-start / race window where streaming starts before detection populates.

(b) Add a message_update() handler that re-evaluates dispatch when an empty/placeholder MESSAGE_CREATE gets patched with mention content. Treats the broker's empty placeholder + later edit as a single logical message-arrival event from the receiver's perspective. Even if (a) misses a case, this is a safety net.

Either fix alone leaves the other gap open; together they close both the proactive and reactive paths.

Environment

  • openab 0.8.4 (HEAD of feat/openab-agent-mcp-resilience, base main)
  • Platform: Discord
  • Config: allow_bot_messages = \"mentions\", multi-bot deployment

Logs / Evidence

Real incident 2026-06-03 — cron-driven multi-bot fact-check workflow in thread 1511750862865956957 (3 bots: Jellyfish orchestrator, Kirin, Mira).

  • Jellyfish's streamed page-request messages mentioning <@Kirin> <@Mira> did not appear in Mira broker's dispatch log during 16:28:13–16:32:04.
  • Discrete non-streamed short messages from the same thread (👍) were dispatched in the same window.
  • Confirmed by inspecting Mira broker logs against Discord message timeline.

Workaround currently in use: callers post triggering messages via raw Discord API POST /channels/<id>/messages so MESSAGE_CREATE is delivered with full content in one event. Works reliably but pushes the responsibility into every multi-bot runbook author.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    auto-closed-staleClosed by stale automation; reopening grants a permanent exemptionbugSomething isn't workingdiscordp1High — address this sprint

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions