Skip to content

[Bug]: Discord thread-bound ACP session receives bot's own ⚙️ system messages, causing infinite turn loop #29325

@yoshikouki

Description

@yoshikouki

Summary

When an ACP session is bound to a Discord thread, OpenClaw's own system messages (⚙️ usage_update, available_commands_update, session active, etc.) are forwarded to the ACP session as user prompts. This causes the agent (Codex) to treat them as tasks, generating responses that are again posted to the thread — creating an infinite loop.

Environment

  • OpenClaw (latest)
  • ACP backend: acpx (v0.1.13)
  • Agent: Codex (gpt-5.3-codex, --full-auto)
  • Channel: Discord (thread-bound session via /acp spawn + thread: true)

Steps to Reproduce

  1. Spawn an ACP session bound to a Discord thread (sessions_spawn runtime="acp", thread=true)
  2. Agent completes the assigned task and posts a result to the thread
  3. OpenClaw posts a system message (e.g. ⚙️ codex session active (auto-unfocus in 24h)...) to the thread
  4. Observe: the system message is forwarded to the ACP session as a new user turn
  5. Codex processes it as a task → posts response → triggers more system messages → loop

Evidence

Session records in ~/.acpx/sessions/ show the pattern clearly:

# Turn 1 (expected — user task)
user: "cd ~/src/.../yoshikouki-com ## レイアウトシステムStep 1-3を実装..."
assistant: "実装完了。110テストpass。"

# Turn 2 (unexpected — system message treated as prompt)
user: "⚙️ codex session active (auto-unfocus in 24h). Messages here go directly to this session. cwd: /home/..."
assistant: "セッション初期化として、ワークスペースの必読ファイルを読み込んで..."

# Turn 3 (loop continues)
user: "⚙️ available_commands_update"
assistant: "依頼内容を実行するため、まずワークスペースの必読ファイルと available_commands_update に..."

# Turn 4+
user: "⚙️ usage_update"
assistant: "usage_update 実行完了..."

Result: 4,456 messages sent to the Discord thread before manual intervention.

Root Cause (Code Analysis)

The Discord channel-level inbound handler correctly filters bot's own messages:

// reply-Deht_wOB.js
if (params.botUserId && author.id === params.botUserId) return null; // ✅ filtered
if (author.bot) {
  if (!allowBots && !sender.isPluralKit) { /* drop */ }  // ✅ filtered
}

However, tryDispatchAcpReply() — the function that forwards inbound messages to the bound ACP session — has no equivalent IsFromBot / IsSelf check. The ⚙️ system messages appear to reach tryDispatchAcpReply via a different code path (thread binding service), bypassing the channel-level bot filter entirely.

Expected Behavior

Bot's own system messages (⚙️ prefix) posted to a thread-bound ACP session's thread should not be forwarded to the ACP session as user prompts.

Actual Behavior

All messages in the bound thread — including OpenClaw's own ⚙️ system messages — are forwarded to the ACP session, causing infinite agent loops.

Workaround

Currently mitigating with acp.stream.coalesceIdleMs: 10000 to reduce flood impact, but this does not prevent the infinite loop itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    staleMarked as stale due to inactivity

    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