Skip to content

iMessage echo loop in self-chat: U+FFFD from attributedBody breaks echo cache text matching #61312

Description

@emmet-bot

Description

In a self-chat scenario (Mac's iMessage handle matches the DM contact), the echo cache fails to detect reflected bot messages due to a Unicode replacement character (U+FFFD / ) prepended during attributedBody text extraction.

Steps to Reproduce

  1. Set up iMessage channel where the Mac's Apple ID phone number matches the DM contact's number (genuine self-chat)
  2. Send a message to the bot
  3. Bot replies via AppleScript → message stored in chat.db with text=NULL, content only in attributedBody
  4. imsg extracts text from attributedBody, which prepends U+FFFD (\xEF\xBF\xBD)
  5. Echo cache stored "Hello" but reflected message arrives as "\uFFFD Hello" → no match → message processed again → infinite echo loop

Evidence

-- Bot messages have text=NULL, content in attributedBody only
SELECT ROWID, is_from_me, text IS NOT NULL as has_text, attributedBody IS NOT NULL as has_body
FROM message WHERE ROWID >= 2665;

-- is_from_me=true (bot):  has_text=0, has_body=1
-- is_from_me=false (user): has_text=1, has_body=1

The normalizeEchoTextKey() function in monitor-provider only does .replace(/\r\n?/g, "\n").trim() — it does not strip U+FFFD characters.

Proposed Fix

In normalizeEchoTextKey(), strip U+FFFD before comparison:

function normalizeEchoTextKey(text: string): string | null {
  if (!text) return null;
  const normalized = text.replace(/\uFFFD/g, '').replace(/\r\n?/g, '\n').trim();
  return normalized || null;
}

Alternatively, the inbound message text extraction in imsg could strip U+FFFD when parsing attributedBody.

Environment

  • OpenClaw: 2026.4.2
  • imsg: 0.4.0
  • macOS (Darwin 24.3.0 arm64)
  • iMessage channel with dmPolicy: pairing

Workaround Attempted

Added responsePrefix: "🐙" to iMessage config — does not help because the U+FFFD breaks text matching regardless of prefix content.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions