#23705: BlueBubbles: enrich webhook group participants from chat metadata
channel: bluebubbles
size: M
Cluster:
Bluebubbles iMessage Fixes
## Summary
Describe the problem and fix in 2–5 bullets:
- Problem: BlueBubbles webhook payloads for some group messages arrive with missing/partial participants, and monitor.ts previously normalized that payload directly without enrichment.
- Why it matters: OpenClaw could miss group members, producing incorrect GroupMembers context and weaker first-contact behavior.
- What changed: Added participant enrichment in monitor.ts to fetch chat metadata before enqueueing, using a new `resolveChatRecordForTarget` helper in send.ts, and then normalizing participant records via exported `normalizeParticipantList`.
- What did NOT change (scope boundary): No allowlist policy changes, no outbound send behavior changes, no auth model changes, and no non-BlueBubbles channel behavior changes.
#### Root causes:
Webhook path queued raw message without participant enrichment
https://github.com/openclaw/openclaw/blob/9363c320d8ffe29290906752fab92621da02c3f7/extensions/bluebubbles/src/monitor.ts#L451-L457
Normalizer falls back to empty participants when webhook/chat payload lacks participant arrays
https://github.com/openclaw/openclaw/blob/9363c320d8ffe29290906752fab92621da02c3f7/extensions/bluebubbles/src/monitor-normalize.ts#L274-L280
## Change Type (select all)
- [x] Bug fix
- [x] Refactor
## Scope (select all touched areas)
- [x] Integrations
## User-visible / Behavior Changes
Incoming BlueBubbles group webhooks with incomplete participant payloads are now enriched from BlueBubbles chat metadata, so GroupMembers/group context includes full roster more reliably.
## Security Impact (required)
- New permissions/capabilities? No
- Secrets/tokens handling changed? No
- New/changed network calls? Yes
- Command/tool execution surface changed? No
- Data access scope changed? Yes
- If any `Yes`, explain risk + mitigation:
- Adds a bounded follow-up BlueBubbles chat query for group messages with sparse participant data (5s timeout only when needed).
- Uses existing account baseUrl + password already configured for BlueBubbles.
- Failure is fail-open to prior behavior (logs verbose failure and continues with webhook payload).
## Repro + Verification
### Environment
OS: Windows 11, BlueBubbles integration target on macOS
Runtime/container: Node 22 + pnpm
Model/provider: N/A
Integration/channel (if any): BlueBubbles
Relevant config (redacted): BlueBubbles account with baseUrl + password, group policy open
### Steps
1. Receive BlueBubbles webhook for a group message where participants is missing/partial.
2. Process webhook through BlueBubbles monitor handler.
3. Inspect normalized context (GroupSubject, GroupMembers) dispatched to runtime.
### Expected
- Group context should include enriched participants from chat metadata when webhook participants are incomplete.
### Actual
- Before fix: often only sender appeared.
- After fix: full participant roster is included when metadata lookup succeeds.
## Evidence
Attach at least one:
- [x] Screenshot/recording
<img width="1177" height="931" alt="BB_Gc" src="https://github.com/user-attachments/assets/3b0adcc7-b922-4dc8-b038-bc9efe21ee03" />
## Human Verification (required)
What you personally verified (not just CI), and how:
- Verified scenarios:
- New monitor regression test passes and asserts GroupMembers includes enriched roster (Alice, Bob, Emily) when webhook lacks participants.
Existing BlueBubbles send/monitor tests still pass.
- Edge cases checked:
- Enrichment is skipped for non-group messages.
- Enrichment is skipped when existing participants are already sufficient.
- Enrichment failure path returns original message (no hard failure).
## Compatibility / Migration
- Backward compatible? Yes
- Config/env changes? No
- Migration needed? No
- If yes, exact upgrade steps: N/A
## Failure Recovery (if this breaks)
- How to disable/revert this change quickly: Revert commit/remove the enrichment call in monitor.ts.
- Files/config to restore: Commit files (monitor.ts, send.ts, monitor-normalize.ts, monitor.test.ts)
- Known bad symptoms reviewers should watch for:
- Increased webhook handling latency in group messages.
- Verbose logs showing repeated participant enrichment failures.
- Group members still missing when BlueBubbles chat query cannot resolve target.
## Risks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write `None`.
- Risk: Extra API lookup per affected group webhook could add latency or transient failure points.
- Mitigation: Strict gating (group-only + sparse participants), 5s timeout, and graceful fallback to original payload.
- Risk: Chat target resolution mismatch across GUID/identifier variants.
- Mitigation: Added matching fallback logic (chat_guid, identifier extraction, identifier fields) and regression coverage.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR enriches BlueBubbles webhook group messages with complete participant rosters by fetching chat metadata when the webhook payload has missing/incomplete participants.
**Key changes:**
- Added `enrichWebhookMessageParticipants` in `monitor.ts:164-220` that queries chat metadata for group messages with sparse participant data
- Refactored `resolveChatGuidForTarget` in `send.ts:319-333` to use new `resolveChatRecordForTarget` helper that returns full chat records instead of just GUIDs
- Exported `normalizeParticipantList` from `monitor-normalize.ts:342` for reuse in enrichment logic
- Added comprehensive test coverage in `monitor.test.ts:1586-1643` verifying enrichment works correctly
**Implementation quality:**
- Bounded enrichment with proper gating (group-only, sparse participants check, 5s timeout)
- Graceful fail-open behavior on errors (logs and continues with original payload)
- Multiple fallback matching strategies for chat lookup (GUID, identifier extraction, chat ID)
- Type-safe implementation using existing BlueBubbles types
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The implementation is well-bounded with proper error handling, comprehensive test coverage, and backward-compatible changes. The enrichment is strictly gated (group-only + sparse participants), has a 5s timeout, and fails gracefully to preserve existing behavior. The refactoring maintains all existing functionality while adding the new enrichment path.
- No files require special attention
<sub>Last reviewed commit: d7994b6</sub>
<!-- greptile_other_comments_section -->
<sub>(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#14429: feat(bluebubbles): handle iMessage edit events in webhook
by westhechiang · 2026-02-12
77.8%
#23483: fix(bluebubbles): key debounce by chat+sender instead of messageId
by saucesteals · 2026-02-22
77.8%
#15240: fix(bluebubbles): URL dropped when sent in same iMessage bubble as ...
by yinghaosang · 2026-02-13
76.2%
#16304: fix(bluebubbles): accept webhook message fields at top level
by MisterGuy420 · 2026-02-14
75.3%
#17779: feat: include group_members in inbound user context metadata
by xpipko · 2026-02-16
75.2%
#23271: fix(chat): strip untrusted metadata blocks from Control UI messages
by lbo728 · 2026-02-22
71.5%
#23226: fix(msteams): proactive messaging, EADDRINUSE fix, tool status, ada...
by TarogStar · 2026-02-22
71.5%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
71.3%
#22260: feat(extensions/deltachat): add Delta.Chat channel extension
by alanz · 2026-02-20
70.8%
#15643: fix(googlechat): DM's works, but groups don't - return add-on actio...
by kamil-rudnicki · 2026-02-13
70.6%