fix(bluebubbles): add staleness gate and GUID dedup for inbound messages#31159
fix(bluebubbles): add staleness gate and GUID dedup for inbound messages#31159dashhuang wants to merge 2 commits intoopenclaw:mainfrom
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 13b5b566ed
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| function _isDuplicateGuid(guid: string | undefined): boolean { | ||
| if (!guid) return false; | ||
| const now = Date.now(); | ||
| if (_dedupGuidCache.size > DEDUP_GUID_CACHE_MAX) { |
There was a problem hiding this comment.
Enforce GUID cache size cap on dedup map
The dedup map is only pruned when size > DEDUP_GUID_CACHE_MAX, and that pruning removes only expired entries, so bursts of >5k unique GUIDs inside the 30-minute TTL will not evict anything and the map will keep growing past the intended cap. This makes the claimed 5k-entry bound ineffective and can cause avoidable memory/CPU growth (full-map scans on each message once over threshold) in busy BlueBubbles deployments.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR adds two defensive guards to prevent BlueBubbles from processing stale or duplicate webhook messages that can be re-delivered after server restarts. Key changes:
Implementation notes:
Production validation:
Confidence Score: 5/5
Last reviewed commit: 13b5b56 |
|
This pull request has been automatically marked as stale due to inactivity. |
13b5b56 to
3c6cf2c
Compare
|
Refreshed this PR onto current \ and resolved the BlueBubbles processing entrypoint drift. The patch remains the same focused behavior change: drop stale inbound messages and dedupe recently seen GUIDs before normal processing.\n\nProduction context is unchanged on our side: this is still a live local delta we carry for BlueBubbles replay/reconnect behavior.\n\nNote: I did not add a dedicated BlueBubbles test here because there still does not appear to be a direct test seam around this webhook path; this refresh is intentionally kept as the minimal one-file patch. |
|
Refreshed this PR onto current main and resolved the BlueBubbles processing entrypoint drift. The patch remains the same focused behavior change: drop stale inbound messages and dedupe recently seen GUIDs before normal processing. Production context is unchanged on our side: this is still a live local delta we carry for BlueBubbles replay/reconnect behavior. Note: I did not add a dedicated BlueBubbles test here because there still does not appear to be a direct test seam around this webhook path; this refresh is intentionally kept as the minimal one-file patch. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3c6cf2cb1d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return age > STALE_MESSAGE_MAX_AGE_MS; | ||
| } | ||
|
|
||
| function _isDuplicateGuid(guid: string | undefined): boolean { |
There was a problem hiding this comment.
Scope GUID dedup cache per account
The new dedup cache is module-global and keyed only by guid, so processMessage will treat the same message ID from different BlueBubbles accounts as a duplicate and drop the later one. This can silently lose legitimate inbound messages in multi-account deployments (for example when two accounts emit overlapping ID formats), because account context is available in processMessage but not part of the dedup key.
Useful? React with 👍 / 👎.
|
I pushed a follow-up test-only fix for the BlueBubbles CI failures. Root cause: the new inbound GUID dedupe cache is module-global, while many BlueBubbles webhook tests reuse the default Fix: add a dedicated test reset hook for the inbound dedupe cache and call it from the shared BlueBubbles monitor test reset helper. This keeps production behavior unchanged and only restores test isolation between cases. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 61acc3b630
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| } | ||
| if (_dedupGuidCache.has(guid)) return true; | ||
| _dedupGuidCache.set(guid, now); |
There was a problem hiding this comment.
Record GUID only after successful processing
_isDuplicateGuid stores every GUID before any downstream work runs, so a transient failure later in processMessage (for example during dispatch/model calls) still burns that GUID. When BlueBubbles re-delivers the same webhook, this guard drops it as a duplicate, turning retryable failures into permanent message loss; this is especially impactful because webhook processing errors are only logged and not retried in the current monitor flow.
Useful? React with 👍 / 👎.
|
This pull request has been automatically marked as stale due to inactivity. |
|
Taking a look at this — going to run it on macOS 26.3 / OpenClaw 2026.4.12 against a BlueBubbles 1.9.x server. The post-restart |
|
Thanks for the original patch @dashhuang — the behavioral goal is exactly right, and this one sat in the queue for too long. I've re-created and improved the fix over in #66230. The key change vs. this one is that the dedupe is now persistent on disk (via Closing in favor of #66230. |
Summary
BlueBubbles MessagePoller uses a 1-week lookback window and can re-fire old messages as webhook events after server restarts or reconnections. OpenClaw currently has no protection against this, causing duplicate message processing and confusing replies to stale messages.
This PR adds two guards at the top of
processMessage()in the BlueBubbles extension:Both guards run before any other processing and log dropped messages via
logVerbose.Context
maxMessageAgefilter for stale re-delivered webhooksDesign decisions
maxMessageAgeSec— simpler, and 5 minutes is generous enough for any legitimate delivery delay. A configurable option could be added later if needed.fromMecheck — stale/duplicatefromMemessages should also be dropped to avoid unnecessary cache operations.Test plan
Fixes #19176
Ref #12053
🤖 Generated with Claude Code