Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
/acp close (and other /acp text commands) issued inside a Discord thread with an active ACP binding never reach handleAcpCommand — they are dispatched to the thread's ACP session and consumed as conversational input by the ACP agent, producing a spurious natural-language reply without actually running the command.
Steps to reproduce
- Configure
channels.discord.threadBindings.enabled: true and channels.discord.threadBindings.spawnAcpSessions: true.
- In a Discord guild text channel, spawn a thread-bound ACP session (e.g.
/acp spawn --thread here with agent claude). Confirm /root/.openclaw/discord/thread-bindings.json contains the new binding entry.
- Inside the newly-created bound Discord thread, send
/acp close as a message (plain text, Discord autocomplete, or slash command dropdown).
- Observe the bot reply, the gateway logs, and
/root/.openclaw/discord/thread-bindings.json.
Expected behavior
handleAcpCloseAction runs, the ACP session is closed, the binding entry is removed from thread-bindings.json, and the bot replies with ✅ Closed ACP session <key>. Removed 1 binding. — the string built at dist/lifecycle-FoOPYxGk.js:566.
Actual behavior
handleAcpCloseAction is never invoked. grep -E "Closed ACP|Removed.*binding|unbind" /root/clawd/logs/openclaw.log returns zero matches for the entire session window.
thread-bindings.json entry is unchanged after the attempt.
- The bot replies with a short natural-language message like
done instead. This is the ACP agent (MiniMax-M2.7 in my case, but any model would exhibit the same hallucination) interpreting the literal text /acp close as a conversational instruction and replying politely. The ACP session remains active and continues routing subsequent thread messages.
OpenClaw version
2026.4.11
Operating system
Ubuntu 22.04.5 LTS (aarch64, Oracle Cloud Always Free VM)
Install method
npm global (/usr/lib/node_modules/openclaw@2026.4.11, launched by systemd openclaw.service)
Model
minimax/MiniMax-M2.7 (not model-specific — any ACP agent will produce a hallucinated natural-language reply to /acp close because the text is routed to it as user input)
Provider / routing chain
MiniMax as primary agent model; Discord channel → spawned thread (bound via spawnAcpSessions: true) → embedded acpx runtime backend → @agentclientprotocol/claude-agent-acp@^0.25.0.
Logs, screenshots, and evidence
Grep of the gateway log across the full window when the user attempted /acp close multiple times:
$ sudo grep -E "Closed ACP|Removed.*binding|unbind" /root/clawd/logs/openclaw.log
(no output)
Relevant code path in the compiled distribution: dist/dispatch-acp-command-bypass-CNBjqOaQ.js::shouldBypassAcpDispatchForCommand:
function shouldBypassAcpDispatchForCommand(ctx, cfg) {
const candidate = resolveCommandCandidateText(ctx);
if (!candidate) return false;
const normalized = candidate.trim();
const allowTextCommands = shouldHandleTextCommands({
cfg,
surface: ctx.Surface ?? ctx.Provider ?? "",
commandSource: ctx.CommandSource
});
if (!normalized.startsWith("/") && maybeResolveTextAlias(candidate, cfg) != null) return allowTextCommands;
if (isResetCommandCandidate(normalized)) return true; // /new, /reset
if (!normalized.startsWith("!")) return false; // ← /acp falls through here
if (!ctx.CommandAuthorized) return false;
if (!isCommandEnabled(cfg, "bash")) return false;
return allowTextCommands;
}
Only /new, /reset, and !-prefix commands bypass the ACP dispatch. /acp ... is not in the bypass list, so any /acp text sent inside a bound thread is handed off to the thread's ACP session instead of being treated as a gateway command.
Impact and severity
Medium-high UX bug. Users cannot close, cancel, steer, change mode, or check status of an ACP session from inside the bound thread where the session is actually running — the most natural place to do so. Every /acp attempt produces a confusing "it replied, but nothing changed" UX: the LLM agent's polite done/好的 hallucination looks like the command succeeded, which compounds the confusion. The only workaround I found was editing thread-bindings.json by hand and restarting the gateway.
Additional information
Confirmed the fix locally by adding /acp to the bypass list:
if (isResetCommandCandidate(normalized)) return true;
+// bypass ACP dispatch for /acp so it reaches handleAcpCommand inside bound threads
+if (/^\/acp(?:\s|$)/i.test(normalized)) return allowTextCommands;
if (!normalized.startsWith("!")) return false;
After this one-line change plus a gateway restart, /acp close issued inside a bound thread reaches handleAcpCommand → handleAcpCloseAction → acpManager.closeSession → getSessionBindingService().unbind, and thread-bindings.json count drops from 2 → 1 as expected.
Related but not duplicate: #59026 is about the inverse direction (parent-channel messages being misrouted to thread after task completion); this issue is about /acp text commands inside the thread never reaching the command handler at all.
I am happy to submit a PR for the above one-line fix if the fix direction is acceptable.
Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
/acp close(and other/acptext commands) issued inside a Discord thread with an active ACP binding never reachhandleAcpCommand— they are dispatched to the thread's ACP session and consumed as conversational input by the ACP agent, producing a spurious natural-language reply without actually running the command.Steps to reproduce
channels.discord.threadBindings.enabled: trueandchannels.discord.threadBindings.spawnAcpSessions: true./acp spawn --thread herewith agentclaude). Confirm/root/.openclaw/discord/thread-bindings.jsoncontains the new binding entry./acp closeas a message (plain text, Discord autocomplete, or slash command dropdown)./root/.openclaw/discord/thread-bindings.json.Expected behavior
handleAcpCloseActionruns, the ACP session is closed, the binding entry is removed fromthread-bindings.json, and the bot replies with✅ Closed ACP session <key>. Removed 1 binding.— the string built atdist/lifecycle-FoOPYxGk.js:566.Actual behavior
handleAcpCloseActionis never invoked.grep -E "Closed ACP|Removed.*binding|unbind" /root/clawd/logs/openclaw.logreturns zero matches for the entire session window.thread-bindings.jsonentry is unchanged after the attempt.doneinstead. This is the ACP agent (MiniMax-M2.7 in my case, but any model would exhibit the same hallucination) interpreting the literal text/acp closeas a conversational instruction and replying politely. The ACP session remains active and continues routing subsequent thread messages.OpenClaw version
2026.4.11
Operating system
Ubuntu 22.04.5 LTS (aarch64, Oracle Cloud Always Free VM)
Install method
npm global (
/usr/lib/node_modules/openclaw@2026.4.11, launched by systemdopenclaw.service)Model
minimax/MiniMax-M2.7(not model-specific — any ACP agent will produce a hallucinated natural-language reply to/acp closebecause the text is routed to it as user input)Provider / routing chain
MiniMax as primary agent model; Discord channel → spawned thread (bound via
spawnAcpSessions: true) → embedded acpx runtime backend →@agentclientprotocol/claude-agent-acp@^0.25.0.Logs, screenshots, and evidence
Grep of the gateway log across the full window when the user attempted
/acp closemultiple times:Relevant code path in the compiled distribution:
dist/dispatch-acp-command-bypass-CNBjqOaQ.js::shouldBypassAcpDispatchForCommand:Only
/new,/reset, and!-prefix commands bypass the ACP dispatch./acp ...is not in the bypass list, so any/acptext sent inside a bound thread is handed off to the thread's ACP session instead of being treated as a gateway command.Impact and severity
Medium-high UX bug. Users cannot close, cancel, steer, change mode, or check status of an ACP session from inside the bound thread where the session is actually running — the most natural place to do so. Every
/acpattempt produces a confusing "it replied, but nothing changed" UX: the LLM agent's politedone/好的hallucination looks like the command succeeded, which compounds the confusion. The only workaround I found was editingthread-bindings.jsonby hand and restarting the gateway.Additional information
Confirmed the fix locally by adding
/acpto the bypass list:After this one-line change plus a gateway restart,
/acp closeissued inside a bound thread reacheshandleAcpCommand→handleAcpCloseAction→acpManager.closeSession→getSessionBindingService().unbind, andthread-bindings.jsoncount drops from 2 → 1 as expected.Related but not duplicate: #59026 is about the inverse direction (parent-channel messages being misrouted to thread after task completion); this issue is about /acp text commands inside the thread never reaching the command handler at all.
I am happy to submit a PR for the above one-line fix if the fix direction is acceptable.