Skip to content

iMessage bridge recovery can dispatch stale inbound backlog as fresh requests #89237

@dwonshin

Description

@dwonshin

Summary

After recovering Messages.app / imsg / IMCore bridge and re-enabling the iMessage channel, OpenClaw processed a burst of old iMessage inbound messages as fresh user requests.

This looks related to, but not fully covered by, the closed/locked stale replay issue #62761. I could not comment there because the issue is locked.

Environment

  • OpenClaw: 2026.5.28 (e932160)
  • imsg: 0.11.0
  • iMessage setup: remote imsg/Messages.app/IMCore bridge via cliPath wrapper
  • channels.imessage.catchup.enabled: not configured/enabled in the active config
  • iMessage queues were empty before and after re-enabling the channel
  • imsg status showed both Basic and Advanced IMCore bridge features available after recovery

What happened

After recovering Messages.app / imsg / IMCore bridge and re-enabling the iMessage channel, a burst of delayed iMessage inbound notifications arrived. They were old/backlog messages, but OpenClaw treated them as fresh inbound user requests and processed them one by one, causing an "iMessage backlog bomb" user experience.

This was not an outbound delivery queue issue: delivery queues were empty, and imsg status was healthy. The problematic path appears to be old inbound rows/notifications reaching the live iMessage monitor path after bridge recovery.

Recovery context

The original incident was a bad Messages.app + imsg bridge lifecycle:

  1. Messages.app had been relaunched normally, which left Advanced IMCore bridge injection unavailable.
  2. Relaunching through imsg launch --verbose restored Advanced features:
    • Basic features: Available
    • Advanced features: Available - IMCore bridge connected
  3. OpenClaw iMessage channel was then re-enabled after confirming queues were empty.
  4. Shortly afterward, stale inbound messages were delivered through the monitor and dispatched to agents as if fresh.

Expected behavior

When the iMessage monitor starts/reconnects after bridge recovery, stale inbound messages should not be dispatched to agents as fresh requests by default. A safe behavior would be one of:

  • a startup high-water rowid/time fence for live watch.subscribe, or
  • a configurable stale-inbound guard before agent dispatch, or
  • making the catchup cursor/stale guard cover this bridge-recovery live-notification path by default.

Suggested guard shape

The right layer seems to be before agent dispatch, not assistant-level NO_REPLY:

  • intercept after parseIMessageNotification(raw) and before inboundDebouncer.enqueue(...) in the iMessage handleMessage path;
  • also consider the catchup replay path so stale replay rows can advance cursor state without dispatching;
  • log concise internal details only;
  • do not create an agent run, specialist routing, typing/read pipeline side effect, or user-visible reply for stale backlog rows.

A candidate policy:

  • startup grace window: first 1-3 minutes after iMessage monitor starts/reconnects;
  • stale threshold: e.g. 15 minutes older than monitor startup/current time;
  • burst heuristic: 5+ stale messages from the same chat within 60 seconds suppresses stale rows from that chat during the grace window;
  • fresh messages from the active conversation continue to dispatch normally.

Why this still matters after the catchup/cursor work

This occurred on 2026.5.28, after #62761 was marked fixed by the current catchup/cursor work. In this setup, catchup.enabled was not set, and the observed problem looked like stale notifications flowing through the live monitor path after bridge recovery rather than explicit catchup replay.

I have not applied any local code patch. I would prefer an upstream fix/maintainer decision because this changes default iMessage startup/reconnect semantics.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions