Bug
In self-chat DM mode (where the bot sends through the user's own iMessage account), outgoing messages echo back as inbound messages and trigger reply loops.
Root Cause
OpenClaw's echo detection in the iMessage monitor has two paths:
-
Message ID matching (60s TTL) — Dead because imsg send RPC returns { ok: true } without a message GUID. normalizeEchoMessageIdKey filters out "ok" and "unknown".
-
Text matching (4s TTL) — Fails for longer/rich-text messages because iMessage prepends 2-3 attributed text bytes to messages in chat.db (e.g. \x93\x62\x02 before the actual text). normalizeEchoTextKey() only does .replace(/\r\n?/g, "\n").trim() — it doesn't strip these non-printable prefix bytes.
Short plain text messages (no attributed text prefix) are correctly caught by the text match path.
Evidence
Raw imsg history --json output shows the prefix bytes:
id=118548, is_from_me=True, text='\x93b\x02Confirmed — full access...' ← has prefix, echo leaked
id=118550, is_from_me=True, text="Echo again. I'll wait..." ← no prefix, echo caught
Cascading errors in gateway log:
ReplyRunAlreadyActiveError: Reply run already active for agent:main:imessage:direct:[email protected]
(every ~1 second)
Fix Options
- imsg CLI:
send RPC should return the message GUID so ID-based echo detection works (60s TTL, most reliable)
- OpenClaw:
normalizeEchoTextKey() should strip non-printable/control characters and attributed text prefix bytes before comparison
- Both (recommended)
Environment
- OpenClaw 2026.4.9
- imsg 0.4.0
- macOS (Messages.app / chat.db)
- Self-chat DM (bot and user share the same iMessage account)
Bug
In self-chat DM mode (where the bot sends through the user's own iMessage account), outgoing messages echo back as inbound messages and trigger reply loops.
Root Cause
OpenClaw's echo detection in the iMessage monitor has two paths:
Message ID matching (60s TTL) — Dead because
imsg sendRPC returns{ ok: true }without a message GUID.normalizeEchoMessageIdKeyfilters out"ok"and"unknown".Text matching (4s TTL) — Fails for longer/rich-text messages because iMessage prepends 2-3 attributed text bytes to messages in chat.db (e.g.
\x93\x62\x02before the actual text).normalizeEchoTextKey()only does.replace(/\r\n?/g, "\n").trim()— it doesn't strip these non-printable prefix bytes.Short plain text messages (no attributed text prefix) are correctly caught by the text match path.
Evidence
Raw
imsg history --jsonoutput shows the prefix bytes:Cascading errors in gateway log:
(every ~1 second)
Fix Options
sendRPC should return the message GUID so ID-based echo detection works (60s TTL, most reliable)normalizeEchoTextKey()should strip non-printable/control characters and attributed text prefix bytes before comparisonEnvironment