openclaw - ✅(Solved) Fix iMessage DM echo: corrupted prefix breaks text-based dedup (v2026.3.31+) [2 pull requests, 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#59973Fetched 2026-04-08 02:38:05
View on GitHub
Comments
1
Participants
2
Timeline
8
Reactions
0
Timeline (top)
cross-referenced ×2mentioned ×2subscribed ×2commented ×1

iMessage DM replies echo back to the agent as new inbound messages. The echo cache fails to match because:

  1. Text TTL too short: SENT_MESSAGE_TEXT_TTL_MS = 4000 (4s) but echoes arrive 5-10s later
  2. Corrupted prefix: Echoed text has leading U+FFFD replacement characters that break exact text matching in normalizeEchoTextKey()

Root Cause

normalizeEchoTextKey() in monitor-provider-*.js only strips whitespace/CR. It does not strip leading U+FFFD/U+FFFE/control chars that iMessage adds to reflected copies. Additionally, the 4s text TTL expires before most echoes arrive.

Fix Action

Workaround

Patch ~/.npm-global/lib/node_modules/openclaw/dist/monitor-provider-*.js directly. Gets overwritten on npm update.

PR fix notes

PR #59980: fix(imessage): catch delayed reflected echoes

Description (problem / solution / changelog)

Summary

  • Problem: iMessage reflected copies can miss dedupe when they arrive after the 4 second text fallback window or when the reflected text starts with replacement/control garbage.
  • Why it matters: missed echo suppression can re-ingest the assistant's own outbound reply as a fresh inbound message and create duplicate or looping replies.
  • What changed: extended the iMessage text fallback TTL to 30 seconds and normalized leading replacement/control characters in both the sent-message echo cache and the self-chat reflection cache.
  • What did NOT change (scope boundary): message ID matching, routing, outbound send behavior, and non-iMessage channels were not changed.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #59973
  • Related #59973
  • This PR fixes a bug or regression

Root Cause / Regression History (if applicable)

  • Root cause: extensions/imessage/src/monitor/echo-cache.ts only normalized CRLF plus trim and expired text fallback entries after 4 seconds, so delayed or prefix-corrupted reflected copies failed exact-text matching.
  • Missing detection / guardrail: coverage preserved the short TTL behavior instead of locking in delayed and corrupted-prefix echoes as dedupe cases.
  • Prior context (git blame, prior PR, issue, or refactor if known): existing iMessage echo-cache tests documented the 4 second text fallback assumption; the linked issue showed reflected text arriving 5 to 10 seconds later with leading U+FFFD characters.
  • Why this regressed now: the current iMessage reflection path can deliver slower or text-corrupted reflected copies than the original fallback assumptions allowed for.
  • If unknown, what was ruled out: ruled out a current fix in the active iMessage cache and inbound-processing paths; the issue behavior still reproduced against the current cache logic.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: extensions/imessage/src/monitor/monitor-provider.echo-cache.test.ts, extensions/imessage/src/monitor/self-chat-cache.test.ts, and extensions/imessage/src/monitor/self-chat-dedupe.test.ts.
  • Scenario the test should lock in: delayed 5 second reflections and reflected copies with leading replacement/control characters should still dedupe in both normal and self-chat flows.
  • Why this is the smallest reliable guardrail: the failure is inside the two normalization and TTL caches, so focused cache tests cover the bug directly without requiring live iMessage infrastructure.
  • Existing test that already covers this (if any): existing iMessage echo-cache tests covered the same caches but encoded the old 4 second miss behavior.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • iMessage now suppresses delayed reflected echoes for longer and also matches reflected copies whose text starts with replacement/control characters.

Diagram (if applicable)

N/A

Security Impact (required)

  • New permissions/capabilities? (Yes/No) No
  • Secrets/tokens handling changed? (Yes/No) No
  • New/changed network calls? (Yes/No) No
  • Command/tool execution surface changed? (Yes/No) No
  • Data access scope changed? (Yes/No) No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS 25.3.0
  • Runtime/container: pnpm workspace, Bun/Node repo defaults
  • Model/provider: N/A
  • Integration/channel (if any): iMessage
  • Relevant config (redacted): default test config

Steps

  1. Cache a recently sent iMessage text in the iMessage echo cache.
  2. Advance time by 5 seconds and query the cache with the same text, or with the same text prefixed by replacement/control characters.
  3. Observe whether the reflected copy is dropped as an echo.

Expected

  • Delayed reflected copies still match the text fallback window.
  • Leading replacement/control garbage does not break text dedupe.

Actual

  • Before this change, the 5 second text-only reflection missed the cache and prefix-corrupted text failed normalization.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: ran pnpm test -- extensions/imessage/src/monitor/monitor-provider.echo-cache.test.ts extensions/imessage/src/monitor/self-chat-cache.test.ts extensions/imessage/src/monitor/self-chat-dedupe.test.ts extensions/imessage/src/monitor/inbound-processing.test.ts; ran pnpm test:extension imessage; ran pnpm build; also sanity-checked the cache behavior directly before the fix and confirmed 5 second text-only and corrupted-prefix matches failed on the old logic.
  • Edge cases checked: self-chat dedupe uses the same normalization fix; message ID retention still outlives the shorter text fallback window.
  • What you did not verify: a live local iMessage roundtrip on macOS.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes/No) Yes
  • Config/env changes? (Yes/No) No
  • Migration needed? (Yes/No) No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: a longer text fallback window could suppress a legitimate repeated identical text message if no message ID is available and it lands inside the same 30 second scope window.
    • Mitigation: the cache stays conversation-scoped, the longer-lived message ID path is unchanged, and the new normalization is limited to leading replacement/control garbage that should not be semantically meaningful for reflected copies.

Notes

  • pnpm check currently fails in unrelated pre-existing extensions/diffs/src/language-hints.test.ts type assertions on this tree; the iMessage-focused tests, extension lane, and pnpm build passed for this PR.
  • AI assistance: used an LLM coding agent for implementation and drafting, then reviewed the touched code paths and validation results before opening this PR.

Made with Cursor

Changed files

  • extensions/imessage/src/monitor/echo-cache.ts (modified, +8/-5)
  • extensions/imessage/src/monitor/monitor-provider.echo-cache.test.ts (modified, +12/-3)
  • extensions/imessage/src/monitor/self-chat-cache.test.ts (modified, +20/-0)
  • extensions/imessage/src/monitor/self-chat-cache.ts (modified, +4/-1)
  • extensions/imessage/src/monitor/self-chat-dedupe.test.ts (modified, +11/-19)

PR #62191: fix(imessage): strip U+FFFD garbage chars from echo text key normalization

Description (problem / solution / changelog)

Summary

U+FFFD replacement characters and C0/C1 control characters injected by imsg when extracting text from NSAttributedString (attributedBody column) break echo cache text-key matching, causing duplicate message delivery.

The echo cache stores clean outbound text, but the reflected inbound copy carries garbage character prefixes — normalizeEchoTextKey() only strips \r\n, so the keys never match and the echo slips through as a new inbound message.

Fix: Strip [\ufffd\ufffe\uffff\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]+ before comparison in normalizeEchoTextKey().

Previously included fixes (now upstream)

The original PR included two additional fixes that have since been merged independently by the OpenClaw team (#61619, #63868, #63980, #63989, #64000):

  • TTL 4s → 30s — shipped in 2026.4.10 (SENT_MESSAGE_TEXT_TTL_MS = 30e3)
  • destination_caller_id self-chat detection — shipped in 2026.4.10 with isSelfChat + isAmbiguousSelfThread

This PR now contains only the remaining unmerged fix.

Changes

  • extensions/imessage/src/monitor/echo-cache.ts: Strip U+FFFD/control chars in normalizeEchoTextKey

Related Issues

Fixes #61312, #61821

Test Plan

  • Confirmed echo cache miss on messages with U+FFFD prefixes (unpatched)
  • Applied patch to compiled dist — garbage-prefixed echoes now correctly matched and suppressed
  • Clean text (no U+FFFD) still matches normally — no regression

Changed files

  • extensions/imessage/src/monitor/echo-cache.ts (modified, +8/-1)

Code Example

const normalized = text.replace(/\r\n?/g, '\n').replace(/^[\ufffd\ufffe\uffff\u0000-\u001f]+/, '').trim();
RAW_BUFFERClick to expand / collapse

Summary

iMessage DM replies echo back to the agent as new inbound messages. The echo cache fails to match because:

  1. Text TTL too short: SENT_MESSAGE_TEXT_TTL_MS = 4000 (4s) but echoes arrive 5-10s later
  2. Corrupted prefix: Echoed text has leading U+FFFD replacement characters that break exact text matching in normalizeEchoTextKey()

Reproduction

  1. Agent sends a long reply (500+ chars) via iMessage DM
  2. Message arrives at recipient
  3. Within 5-30 seconds, the same message appears as a new inbound message with is_from_me=0
  4. Text starts with garbled chars (\ufffd, shown as ) followed by a truncated copy of the original
  5. Echo cache lookup fails → message processed as new user input → agent replies → loop

Affected versions

  • v2026.3.31 (confirmed)
  • v2026.4.2 (confirmed, same code path)

Root cause

normalizeEchoTextKey() in monitor-provider-*.js only strips whitespace/CR. It does not strip leading U+FFFD/U+FFFE/control chars that iMessage adds to reflected copies. Additionally, the 4s text TTL expires before most echoes arrive.

Suggested fix

  1. Increase SENT_MESSAGE_TEXT_TTL_MS to 30s (matching the WhatsApp echo fix pattern)
  2. Strip leading replacement/control characters in normalizeEchoTextKey():
const normalized = text.replace(/\r\n?/g, '\n').replace(/^[\ufffd\ufffe\uffff\u0000-\u001f]+/, '').trim();

Workaround

Patch ~/.npm-global/lib/node_modules/openclaw/dist/monitor-provider-*.js directly. Gets overwritten on npm update.

Related

  • #41330 (original echo regression)
  • #33295 (echo loop hardening)
  • #54570 (WhatsApp echo fix)
  • #53624 (WhatsApp group echo)

/cc @vincentkoc @joelnishanth

extent analysis

TL;DR

Increase the SENT_MESSAGE_TEXT_TTL_MS to 30s and modify the normalizeEchoTextKey() function to strip leading replacement and control characters to prevent echo cache mismatches.

Guidance

  • Increase the SENT_MESSAGE_TEXT_TTL_MS to 30s to ensure the echo cache does not expire before the echoed message arrives.
  • Modify the normalizeEchoTextKey() function to strip leading replacement and control characters, such as U+FFFD, using a regular expression like /^[\ufffd\ufffe\uffff\u0000-\u001f]+/.
  • Verify the fix by testing the iMessage DM reply functionality and checking for echo cache mismatches.
  • Consider patching the monitor-provider-*.js file directly as a temporary workaround, but note that this will be overwritten on npm update.

Example

const normalized = text.replace(/\r\n?/g, '\n').replace(/^[\ufffd\ufffe\uffff\u0000-\u001f]+/, '').trim();

Notes

The suggested fix assumes that the issue is specific to the iMessage DM reply functionality and may not apply to other messaging platforms. Additionally, the temporary workaround of patching the monitor-provider-*.js file may not be a long-term solution.

Recommendation

Apply the suggested fix by increasing the SENT_MESSAGE_TEXT_TTL_MS to 30s and modifying the normalizeEchoTextKey() function, as this addresses the root cause of the issue and provides a more robust solution than the temporary workaround.

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 - ✅(Solved) Fix iMessage DM echo: corrupted prefix breaks text-based dedup (v2026.3.31+) [2 pull requests, 1 comments, 2 participants]