openclaw - 💡(How to fix) Fix Discord thread-bound ACP sessions (mode=session) deliver each response twice [1 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#60780Fetched 2026-04-08 02:47:20
View on GitHub
Comments
0
Participants
1
Timeline
0
Reactions
0
Author
Participants

Thread-bound ACP sessions (sessions_spawn with runtime: "acp", thread: true, mode: "session") send every response twice to the Discord thread, ~300–800ms apart. Both messages are identical and delivered via the same webhook persona.

Root Cause

Two delivery paths fire for the same response:

  1. Inline delivery (deliver: true): resolveAcpSpawnBootstrapDeliveryPlan sets useInlineDelivery = true when spawnMode === "session" and requester origin channel exists. The gateway auto-reply sends the response directly to the thread via the webhook.

  2. Announce delivery: After the turn completes, the subagent announce machinery (deliverSubagentAnnouncement) fires. The Discord subagent_delivery_target hook resolves the thread binding and delivers a second copy to the same thread.

For mode: "session", shouldKeepThreadBindingAfterRun returns true (preventing lifecycle hooks), but the per-turn announce still fires because each user message triggers a new agent turn that completes.

Key code paths:

  • resolveAcpSpawnBootstrapDeliveryPlan (line ~21874): sets useInlineDelivery for session mode
  • resolveSpawnMode$1 (line ~21548): thread: true + no explicit mode → defaults to "session"
  • runSubagentAnnounceDispatch (line ~10490): dispatches announce after turn

Fix Action

Fix / Workaround

Key code paths:

  • resolveAcpSpawnBootstrapDeliveryPlan (line ~21874): sets useInlineDelivery for session mode
  • resolveSpawnMode$1 (line ~21548): thread: true + no explicit mode → defaults to "session"
  • runSubagentAnnounceDispatch (line ~10490): dispatches announce after turn

Workarounds

Code Example

"channels.discord.threadBindings.spawnAcpSessions": true,
   "channels.discord.threadBindings.enabled": true,
   "acp.enabled": true

---

{
     "id": "claude",
     "runtime": { "type": "acp", "acp": { "agent": "claude", "backend": "acpx", "mode": "persistent" } }
   }

---

[ws] ⇄ res ✓ agent 119ms runId=announce:v1:agent:claude:acp:66d6895d-...:015b9656-...

---

{
  "acp": {
    "enabled": true,
    "backend": "acpx",
    "defaultAgent": "claude",
    "allowedAgents": ["claude", "codex"],
    "maxConcurrentSessions": 8
  },
  "channels": {
    "discord": {
      "threadBindings": {
        "enabled": true,
        "spawnAcpSessions": true
      }
    }
  },
  "session": {
    "threadBindings": {
      "enabled": true,
      "idleHours": 24,
      "maxAgeHours": 0
    }
  }
}
RAW_BUFFERClick to expand / collapse

Summary

Thread-bound ACP sessions (sessions_spawn with runtime: "acp", thread: true, mode: "session") send every response twice to the Discord thread, ~300–800ms apart. Both messages are identical and delivered via the same webhook persona.

Environment

  • OpenClaw: 2026.4.2 (d74a122)
  • acpx: bundled
  • ACP agent: Claude Code (OAuth, claude-opus-4-6)
  • Channel: Discord (gateway bot)
  • OS: macOS 26.4 (arm64)

Reproduction

  1. Enable thread-bound ACP spawns:
    "channels.discord.threadBindings.spawnAcpSessions": true,
    "channels.discord.threadBindings.enabled": true,
    "acp.enabled": true
  2. Agent definition (agents.list[]):
    {
      "id": "claude",
      "runtime": { "type": "acp", "acp": { "agent": "claude", "backend": "acpx", "mode": "persistent" } }
    }
  3. From a main session, spawn an ACP session with sessions_spawn(runtime: "acp", agentId: "claude", thread: true, mode: "session", task: "...")
  4. Send any message in the resulting Discord thread
  5. Every response appears twice (~300–800ms gap, identical content, same webhook)

Evidence

Discord thread 1489899150219808790 shows consistent duplication:

User messageResponse 1 timestampResponse 2 timestampDelta
今日の作業内容を教えて08:09:44.27608:09:45.093817ms
私は誰?08:12:16.56508:12:16.900335ms
ブラウザ起動して08:16:24.27808:16:24.619341ms

Both messages come from the same webhook (id: 1489559854879080528, username: 🤖 claude).

Gateway log shows an announce run alongside the normal inline delivery:

[ws] ⇄ res ✓ agent 119ms runId=announce:v1:agent:claude:acp:66d6895d-...:015b9656-...

Analysis

Two delivery paths fire for the same response:

  1. Inline delivery (deliver: true): resolveAcpSpawnBootstrapDeliveryPlan sets useInlineDelivery = true when spawnMode === "session" and requester origin channel exists. The gateway auto-reply sends the response directly to the thread via the webhook.

  2. Announce delivery: After the turn completes, the subagent announce machinery (deliverSubagentAnnouncement) fires. The Discord subagent_delivery_target hook resolves the thread binding and delivers a second copy to the same thread.

For mode: "session", shouldKeepThreadBindingAfterRun returns true (preventing lifecycle hooks), but the per-turn announce still fires because each user message triggers a new agent turn that completes.

Key code paths:

  • resolveAcpSpawnBootstrapDeliveryPlan (line ~21874): sets useInlineDelivery for session mode
  • resolveSpawnMode$1 (line ~21548): thread: true + no explicit mode → defaults to "session"
  • runSubagentAnnounceDispatch (line ~10490): dispatches announce after turn

Expected behavior

Each response should be delivered exactly once to the Discord thread.

Workarounds

  • Set channels.discord.threadBindings.spawnAcpSessions: false (disables thread-bound ACP entirely)
  • Spawn with explicit mode: "run" instead of mode: "session" (loses persistent conversation)
  • No config-only fix exists to keep mode: "session" without duplication

Config (relevant subset)

{
  "acp": {
    "enabled": true,
    "backend": "acpx",
    "defaultAgent": "claude",
    "allowedAgents": ["claude", "codex"],
    "maxConcurrentSessions": 8
  },
  "channels": {
    "discord": {
      "threadBindings": {
        "enabled": true,
        "spawnAcpSessions": true
      }
    }
  },
  "session": {
    "threadBindings": {
      "enabled": true,
      "idleHours": 24,
      "maxAgeHours": 0
    }
  }
}

No duplicate bindings or routes exist; only one Discord account is configured.

extent analysis

TL;DR

To fix the issue of duplicate responses in Discord threads, set mode to "run" instead of "session" when spawning an ACP session or disable thread-bound ACP sessions by setting channels.discord.threadBindings.spawnAcpSessions to false.

Guidance

  • Identify the root cause: The issue arises from two delivery paths firing for the same response, namely inline delivery and announce delivery.
  • Verify the issue: Check the Discord thread for duplicate responses and the gateway log for an announce run alongside the normal inline delivery.
  • Mitigate the issue: Set mode to "run" when spawning an ACP session to lose persistent conversation but avoid duplication.
  • Workaround: Disable thread-bound ACP sessions by setting channels.discord.threadBindings.spawnAcpSessions to false, which will entirely disable thread-bound ACP.

Example

No code snippet is provided as the issue is related to configuration and not code.

Notes

The provided workarounds have trade-offs, such as losing persistent conversation or disabling thread-bound ACP entirely. A config-only fix does not exist to keep mode: "session" without duplication.

Recommendation

Apply the workaround by setting mode to "run" when spawning an ACP session, as it is a more targeted solution that avoids disabling thread-bound ACP entirely. However, this comes at the cost of losing persistent conversation.

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…

FAQ

Expected behavior

Each response should be delivered exactly once to the Discord thread.

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 - 💡(How to fix) Fix Discord thread-bound ACP sessions (mode=session) deliver each response twice [1 participants]