openclaw - 💡(How to fix) Fix [Bug]: BlueBubbles tapback events reach before_dispatch as synthesized text bodies; structural fields (associatedMessageType / isTapback) are stripped, enabling empty-turn synthetic leak [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#76158Fetched 2026-05-03 04:41:41
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
2
Author
Timeline (top)
closed ×1commented ×1unsubscribed ×1

The BlueBubbles channel plugin parses inbound tapback / reaction events into structured form (resolveTapbackContext, isTapbackAssociatedType, associatedMessageType 2000–2999 = added / 3000–3999 = removed), but the structural fields are not propagated into the before_dispatch plugin hook event. Plugins downstream see only the synthesized text body ("reacted with <emoji>" / "removed <emoji> reaction") and have no structural primitive to admit/filter on.

This combined with a separate runtime quirk produces a real user-visible synthetic leak: a tapback inbound creates an empty-turn agent run, and the runtime's empty-turn fallback emits "⚠️ Agent couldn't generate a response. Please try again." as the user-facing reply, delivered through native BlueBubbles (which bypasses any downstream klawbi-runner-style outbound sanitize).

Root Cause

Root cause (two-part)

Fix Action

Fix / Workaround

The BlueBubbles channel plugin parses inbound tapback / reaction events into structured form (resolveTapbackContext, isTapbackAssociatedType, associatedMessageType 2000–2999 = added / 3000–3999 = removed), but the structural fields are not propagated into the before_dispatch plugin hook event. Plugins downstream see only the synthesized text body ("reacted with <emoji>" / "removed <emoji> reaction") and have no structural primitive to admit/filter on.

But by the time the dispatch path constructs the before_dispatch plugin event (dispatch- prefixed bundle, line referenced in 2026.4.27), the event passed to plugins is reduced to:

associatedMessageType / isTapback are not present. Plugins that hook before_dispatch (e.g. silent-operator/admission gates) must work from the synthesized text body and have no structural primitive available.

Code Example

{
  text,                         // synthesized: "reacted with 👍"
  associatedMessageType,        // numeric (2000–2999 added, 3000–3999 removed)
  associatedMessageGuid,
  associatedMessageEmoji,
  isTapback,                    // boolean
  ...
}

---

{
  content,
  body,
  channel,
  sessionKey,
  senderId,
  isGroup,
  timestamp,
}
RAW_BUFFERClick to expand / collapse

Summary

The BlueBubbles channel plugin parses inbound tapback / reaction events into structured form (resolveTapbackContext, isTapbackAssociatedType, associatedMessageType 2000–2999 = added / 3000–3999 = removed), but the structural fields are not propagated into the before_dispatch plugin hook event. Plugins downstream see only the synthesized text body ("reacted with <emoji>" / "removed <emoji> reaction") and have no structural primitive to admit/filter on.

This combined with a separate runtime quirk produces a real user-visible synthetic leak: a tapback inbound creates an empty-turn agent run, and the runtime's empty-turn fallback emits "⚠️ Agent couldn't generate a response. Please try again." as the user-facing reply, delivered through native BlueBubbles (which bypasses any downstream klawbi-runner-style outbound sanitize).

Reproduction (verified incident)

  • Date: 2026-05-01 17:10:51Z
  • Channel: BlueBubbles iMessage group (chat chat813254241117177218)
  • Stimulus: at 17:10:45Z a participant reacted with 👍 to a prior message
  • Result: at 17:10:51Z OpenClaw emitted "⚠️ Agent couldn't generate a response. Please try again." as a real channel message, delivered through the native BlueBubbles surface
  • Deployed version: OpenClaw 2026.4.27 (cbc2ba0) — i.e. after the changelog entries that target the partial-reply phantom-fallback class

What's already covered (and why this is distinct)

The following changelog entries protect some synthetic-fallback variants but do not cover the tapback-triggered empty-turn case:

  • 2026.4.22 #70425 / #70396 — suppresses warning when assistant already delivered user-visible content via a messaging tool and turn ended cleanly.
  • 2026.4.24 #70623 — suppresses phantom fallback after reply already committed via messaging tool.
  • 2026.4.26 #72751 — retries replay-safe empty stop turns once when continuation succeeds.

All three protect cases where a reply / continuation existed in-turn. None covers the case where the turn produces zero user-visible content from the agent and the runtime's empty-turn fallback synthetic is emitted instead.

Root cause (two-part)

(A) BlueBubbles plugin event-shape stripping

In the BB plugin normalizer (probe-prefixed bundle), normalizeBlueBubblesMessage parses tapbacks into:

{
  text,                         // synthesized: "reacted with 👍"
  associatedMessageType,        // numeric (2000–2999 added, 3000–3999 removed)
  associatedMessageGuid,
  associatedMessageEmoji,
  isTapback,                    // boolean
  ...
}

But by the time the dispatch path constructs the before_dispatch plugin event (dispatch- prefixed bundle, line referenced in 2026.4.27), the event passed to plugins is reduced to:

{
  content,
  body,
  channel,
  sessionKey,
  senderId,
  isGroup,
  timestamp,
}

associatedMessageType / isTapback are not present. Plugins that hook before_dispatch (e.g. silent-operator/admission gates) must work from the synthesized text body and have no structural primitive available.

(B) Empty-turn fallback eligibility

A tapback inbound, once delivered as a flat text body, is admissible to the agent run loop. With nothing actionable to respond to, the turn produces no assistant content. The runtime's empty-turn synthetic suppression covers partial-reply cases (per the changelog references above) but not the zero-output case, so "⚠️ Agent couldn't generate a response." is emitted and delivered.

Proposed fix

Two complementary asks; the structural one is the durable answer:

  1. Pass tapback / reaction structural fields into before_dispatch (and ideally message_received):

    • associatedMessageType: number | undefined
    • associatedMessageGuid: string | undefined
    • associatedMessageEmoji: string | undefined
    • isTapback: boolean | undefined

    This lets plugins do clean structural admission (e.g. default-deny on non-text events) without text-pattern heuristics on the synthesized body.

  2. Empty-turn synthetic suppression for the no-visible-content case: When a turn ends with stopReason=stop (or equivalent), no messaging-tool delivery occurred in-turn, AND the runtime is about to emit the generic "Agent couldn't generate a response" fallback — suppress the fallback rather than surface it as a channel message.

Local mitigation already in place (workaround until upstream lands)

We've shipped a downstream regex-anchored stopgap at the runner reply-gate that catches the BlueBubbles-normalizer text shapes (^reacted with X / ^removed X reaction) and silences dispatch. This is a heuristic on a derived body string and is acceptable as a stopgap; a structural primitive in the dispatch event would be the durable fix.

Related issues

  • #60274imsg rpc provider does not filter tapback reactions (sibling problem on a different iMessage transport; same fix-class)
  • #70628 — Telegram DM fabricates silent-reply chatter for no-visible-response turns (same empty-turn class, different channel)
  • #39031 — Feature: forward iMessage tapback reactions to agent (orthogonal but related)
  • #60446 — Feature: surface tapback events to agent

Environment

  • OpenClaw: 2026.4.27 (cbc2ba0)
  • Channel: BlueBubbles
  • iMessage transport: BlueBubbles server (not imsg rpc — this issue is distinct from #60274 in that respect)
  • Group context: mixed-human iMessage group (klawbi accountId: klawbi)

Acceptance hints

  • Plugins hooking before_dispatch on a bluebubbles channel event for an inbound with associatedMessageType in the 2000-3999 range can read that field directly and short-circuit to silent without text matching
  • Optional: an explicit eventType: "tapback" | "text" | ... field would generalize beyond the numeric range check and align with future work in #39031 / #60446

extent analysis

TL;DR

Pass tapback/reaction structural fields into the before_dispatch plugin event and suppress empty-turn synthetics for no-visible-content cases to fix the issue.

Guidance

  • Modify the dispatch- prefixed bundle to include associatedMessageType, associatedMessageGuid, associatedMessageEmoji, and isTapback in the before_dispatch plugin event.
  • Update the runtime to suppress the empty-turn synthetic fallback when a turn ends with no visible content and no messaging-tool delivery occurred.
  • Verify that plugins can read the associatedMessageType field directly and short-circuit to silent without text matching.
  • Consider adding an explicit eventType field to generalize beyond the numeric range check.

Example

// Modified before_dispatch plugin event
{
  content,
  body,
  channel,
  sessionKey,
  senderId,
  isGroup,
  timestamp,
  associatedMessageType, // added
  associatedMessageGuid, // added
  associatedMessageEmoji, // added
  isTapback, // added
}

Notes

The proposed fix requires changes to the BlueBubbles plugin and the runtime. The local mitigation already in place can serve as a temporary workaround until the upstream fix is implemented.

Recommendation

Apply the proposed fix to pass tapback/reaction structural fields into the before_dispatch plugin event and suppress empty-turn synthetics for no-visible-content cases, as it provides a durable solution to the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Bug]: BlueBubbles tapback events reach before_dispatch as synthesized text bodies; structural fields (associatedMessageType / isTapback) are stripped, enabling empty-turn synthetic leak [1 comments, 2 participants]