[Bug] Gateway swallows user-visible reply when an agent's exec stdout matches "configuration file" — replies=2 puts ⚠️ 🛠️ first, the actual reply second
Environment
- OpenClaw version: 2026.6.10-beta.2 (this is the version we observed the bug on; we have not bisected earlier versions)
- Feishu plugin:
@openclaw/[email protected]
- Channel: Feishu DM, single account (
feishu[tester])
- Model that produced the run:
zai/glm-5.2
- Host: macOS 15.5.0, Node v26.3.0
- Observed at: 2026-06-24 08:32–08:34 Asia/Shanghai (tester run); a second instance at 09:21–09:23 in the same DM with the same
replies=2 shape
Summary
When an agent's exec step produces stdout that contains the substring configuration file at <path>, the gateway populates trace.artifacts.data.lastToolError with that stdout and sets mutatingAction: true. The turn's finalStatus is success and every item completes, but the channel still receives replies=2 — the first reply is a ⚠️ 🛠️ warning card referencing the exec step, and the second reply is the agent's actual final text.
From the user's perspective in the Feishu DM, the warning card sits above the actual reply in the conversation list. The user opens the DM, sees only the warning, and concludes the agent "errored and dropped the message" — even though the real reply IS delivered, just visually displaced.
This affects the user in two ways:
- For runs the user is waiting on (the tester's 8:32 case): the user thinks the agent errored and re-prompts, producing duplicate work and a duplicate
replies=2 for the follow-up turn.
- For the user's own follow-up messages in the same DM (the 9:21 case below): the user sends a short message, the agent replies with a brief text, but again
replies=2 is reported, and the user only sees the warning card. The actual agent reply is delivered as the second card and is not visible at the top of the DM.
We do not have evidence yet that the second pattern is triggered by an exec step with the same stdout shape — we are reporting both instances because the observable symptom (replies=2, warning card first, real reply second) is the same. Both could plausibly be the same gateway behavior surfacing warnings for two different underlying conditions; we have not bisected to confirm.
First observed instance (08:32–08:34, tester)
During a multi-tool agent turn, one of the agent's exec steps ran cat ~/Library/Application Support/ngrok/ngrok.yml. The command succeeded and its stdout began with the literal text Valid configuration file at /Users/penny/Library/Application Support/ngrok/ngrok.yml (this is a normal ngrok startup/log message, not an error).
After the turn completed, the gateway's trace.artifacts.data.lastToolError was populated with:
{
"toolName": "exec",
"meta": "run # → print text",
"error": "Valid configuration file at /Users/penny/Library/Application Support/ngrok/ngrok.yml",
"mutatingAction": true,
"actionFingerprint": "tool=exec|meta=run # → print text"
}
The turn's finalStatus was success (all 26 items started and completed). However, the Feishu channel received replies=2 from the gateway instead of replies=1. From the user's perspective in the Feishu DM, the first card was a ⚠️ 🛠️ warning referencing the cat step, and the second card was the agent's actual final reply containing the URLs the user asked for. The warning card sits above the reply in the DM list, so at a glance the user sees "agent just errored".
Second observed instance (09:21–09:23, config agent, no exec trigger found)
2026-06-24T09:21:52.156+08:00 [feishu] feishu[config]: dispatching to agent
2026-06-24T09:21:58.597+08:00 [feishu] feishu[config] Started streaming: cardId=7654766098637851612
... <agent does its work, does NOT visibly invoke an exec step with "configuration file" stdout at this point, but we have not pulled the trajectory for this run yet> ...
2026-06-24T09:23:23.211+08:00 [feishu] feishu[config]: dispatch complete (queuedFinal=true, replies=2)
2026-06-24T09:23:24.531+08:00 [feishu] feishu[config] Closed streaming
User-visible result: user only saw a ⚠️ 🛠️ card in the Feishu DM. The actual reply ("Issue #96239 created, link: ...") was the second card and was visually below the warning. We have not yet pulled the trajectory for this second run to confirm whether the same lastToolError / mutatingAction mechanism fired, or whether a different lastToolError payload was responsible. We are including this instance to demonstrate that the bug bites for short, single-shot agent replies too, not just multi-tool turns.
Gateway log excerpt (08:32 tester run)
2026-06-24T08:32:08.594+08:00 [feishu] feishu[tester]: received message from <user_id> in <chat_id> (p2p)
2026-06-24T08:32:08.631+08:00 [feishu] feishu[tester]: dispatching to agent (session=agent:tester:main)
2026-06-24T08:32:20.444+08:00 [feishu] feishu[tester] Started streaming: cardId=7654753303888907225, messageId=om_x100b6c8617f2fca0b32e362961f959a
... ~2m of exec/memory_search/gateway/process activity ...
2026-06-24T08:34:22.875+08:00 [feishu] feishu[tester]: dispatch complete (queuedFinal=true, replies=2)
2026-06-24T08:34:37.825+08:00 [feishu] feishu[tester] Closed streaming: cardId=7654753303888907225
Trajectory excerpt (08:32 tester run, 8:32:09 → 8:34:22 UTC)
trace.artifacts itemLifecycle: {startedCount: 26, completedCount: 26, activeCount: 0} — the run completed normally.
trace.artifacts toolMetas (16 entries, in order):
[0] wiki_search: 外网调试地址 远程访问
[1] memory_search: 外网调试地址 远程访问 URL
[2] gateway: gateway.externalUrl
[3] memory_search: tunnel ngrok cloudflare 外网 端口 调试
[4] exec: print text → show ~/.openclaw/config.yaml ...
[5] exec: run # -> show first 80 lines ...
[6] exec: run # → print text → run ps aux -> search "grep" ...
[7] exec: run # → print text → print text → run ps aux ...
[8] memory_search: 外网 IP 端口转发 18765 18789 公网访问 remote
[9] exec: # 查之前是否用过ngrok/cloudflared建隧道 ...
[10] exec: run # → show ~/.ngrok2/ngrok.yml ...
[11] exec: run # → print text
[12] exec: show ~/Library/Application Support/ngrok/ngrok.yml
[13] exec: # 启动 ngrok 隧道到 Polyphone 18765 ...
[14] process: crisp-coral
[15] exec: fetch http://localhost:18765/
trace.artifacts lastToolError: as shown above.
Expected behavior
- A successful agent turn (
finalStatus: success, all items completed) should produce exactly one user-visible reply card, not two. This applies to both multi-tool turns and short single-shot replies.
- An
exec step that only cats / reads a file and whose stdout happens to mention "configuration file" should not be classified as a mutatingAction. The agent did not modify the file.
- If a warning is generated for any reason, it should not pre-empt the agent's actual final reply in the channel card ordering.
- If the gateway cannot determine whether a tool call mutated state, the default should be "do not warn the user" rather than "warn the user with a visually-prior card".
Actual behavior
- The Feishu DM shows a
⚠️ 🛠️ warning card referencing the cat ngrok.yml step (first instance) — and the user only sees a ⚠️ 🛠️ card with no obvious reason (second instance, mechanism not yet confirmed) — before the agent's actual final reply.
- The gateway reports
replies=2 for turns that, from the agent's perspective, completed exactly once.
- In both observed instances, the user had to re-prompt the agent because they thought the reply had been lost. The re-prompt produced a second
replies=2 shape, compounding the visual confusion.
Observations about the classification (first instance)
- The string
Valid configuration file at <path> in the error field matches the stdout of the previous exec step verbatim. This suggests lastToolError may be populating from a heuristic that scans exec stdout for substrings like "configuration file" rather than from a real tool error.
mutatingAction: true is set even though the command was a read-only cat (no write/redirect/in-place edit verb).
- The
run succeeded — itemLifecycle.activeCount: 0 and finalStatus: "success".
We have not yet identified which subsystem of the gateway sets these fields, and we are not sure if the same code path is responsible for both populating lastToolError and emitting the ⚠️ 🛠️ warning. We are filing this with whatever concrete evidence we have.
Impact
- Data loss: None in either instance. The agent's reply is delivered, just out of order and visually buried.
- User experience: The user has to look past the warning card to find the real reply, and may re-prompt because they think the reply was lost. In both instances we observed, the user re-prompted, which produced a follow-up turn that also suffered from the same
replies=2 shape.
- Reproducibility: We have observed this on at least two agents (
tester and config) in the same Feishu DM in the same hour, with two different trigger shapes. We do not yet have a controlled reproduction script.
What we have not yet verified
- Whether this reproduces on OpenClaw versions earlier than 2026.6.10-beta.2.
- Whether the same
lastToolError / mutatingAction behavior occurs on other channels (Discord / Telegram / WebChat).
- Whether the second instance (09:21, config agent) was triggered by the same
exec stdout heuristic or by a different code path. We have not yet pulled the trajectory for that run.
- Whether the heuristic is specifically targeting the substring
configuration file or something else about the exec metadata (run # → print text).
- Which subsystem file/code path is responsible.
We can run additional bisects or supply more trajectory files if maintainers want to narrow this down.
Related issues (already triaged, none matched our case)
Reporter
- OpenClaw + Feishu setup on a single Mac mini, 7 agents.
- Trajectory files retained per session under
~/.openclaw/agents/<agent>/sessions/.
- Gateway log at
~/Library/Logs/openclaw/gateway.log.
[Bug] Gateway swallows user-visible reply when an agent's exec stdout matches "configuration file" — replies=2 puts⚠️ 🛠️ first, the actual reply second
Environment
@openclaw/[email protected]feishu[tester])zai/glm-5.2replies=2shapeSummary
When an agent's
execstep produces stdout that contains the substringconfiguration file at <path>, the gateway populatestrace.artifacts.data.lastToolErrorwith that stdout and setsmutatingAction: true. The turn'sfinalStatusissuccessand every item completes, but the channel still receivesreplies=2— the first reply is a⚠️ 🛠️warning card referencing theexecstep, and the second reply is the agent's actual final text.From the user's perspective in the Feishu DM, the warning card sits above the actual reply in the conversation list. The user opens the DM, sees only the warning, and concludes the agent "errored and dropped the message" — even though the real reply IS delivered, just visually displaced.
This affects the user in two ways:
replies=2for the follow-up turn.replies=2is reported, and the user only sees the warning card. The actual agent reply is delivered as the second card and is not visible at the top of the DM.We do not have evidence yet that the second pattern is triggered by an
execstep with the same stdout shape — we are reporting both instances because the observable symptom (replies=2, warning card first, real reply second) is the same. Both could plausibly be the same gateway behavior surfacing warnings for two different underlying conditions; we have not bisected to confirm.First observed instance (08:32–08:34, tester)
During a multi-tool agent turn, one of the agent's
execsteps rancat ~/Library/Application Support/ngrok/ngrok.yml. The command succeeded and its stdout began with the literal textValid configuration file at /Users/penny/Library/Application Support/ngrok/ngrok.yml(this is a normalngrokstartup/log message, not an error).After the turn completed, the gateway's
trace.artifacts.data.lastToolErrorwas populated with:{ "toolName": "exec", "meta": "run # → print text", "error": "Valid configuration file at /Users/penny/Library/Application Support/ngrok/ngrok.yml", "mutatingAction": true, "actionFingerprint": "tool=exec|meta=run # → print text" }The turn's
finalStatuswassuccess(all 26 items started and completed). However, the Feishu channel receivedreplies=2from the gateway instead ofreplies=1. From the user's perspective in the Feishu DM, the first card was a⚠️ 🛠️warning referencing thecatstep, and the second card was the agent's actual final reply containing the URLs the user asked for. The warning card sits above the reply in the DM list, so at a glance the user sees "agent just errored".Second observed instance (09:21–09:23, config agent, no exec trigger found)
User-visible result: user only saw a
⚠️ 🛠️card in the Feishu DM. The actual reply ("Issue #96239 created, link: ...") was the second card and was visually below the warning. We have not yet pulled the trajectory for this second run to confirm whether the samelastToolError/mutatingActionmechanism fired, or whether a differentlastToolErrorpayload was responsible. We are including this instance to demonstrate that the bug bites for short, single-shot agent replies too, not just multi-tool turns.Gateway log excerpt (08:32 tester run)
Trajectory excerpt (08:32 tester run, 8:32:09 → 8:34:22 UTC)
trace.artifactsitemLifecycle:{startedCount: 26, completedCount: 26, activeCount: 0}— the run completed normally.trace.artifactstoolMetas(16 entries, in order):trace.artifactslastToolError: as shown above.Expected behavior
finalStatus: success, all items completed) should produce exactly one user-visible reply card, not two. This applies to both multi-tool turns and short single-shot replies.execstep that onlycats / reads a file and whose stdout happens to mention "configuration file" should not be classified as amutatingAction. The agent did not modify the file.Actual behavior
⚠️ 🛠️warning card referencing thecat ngrok.ymlstep (first instance) — and the user only sees a⚠️ 🛠️card with no obvious reason (second instance, mechanism not yet confirmed) — before the agent's actual final reply.replies=2for turns that, from the agent's perspective, completed exactly once.replies=2shape, compounding the visual confusion.Observations about the classification (first instance)
Valid configuration file at <path>in theerrorfield matches the stdout of the previousexecstep verbatim. This suggestslastToolErrormay be populating from a heuristic that scansexecstdout for substrings like "configuration file" rather than from a real tool error.mutatingAction: trueis set even though the command was a read-onlycat(no write/redirect/in-place edit verb).runsucceeded —itemLifecycle.activeCount: 0andfinalStatus: "success".We have not yet identified which subsystem of the gateway sets these fields, and we are not sure if the same code path is responsible for both populating
lastToolErrorand emitting the⚠️ 🛠️warning. We are filing this with whatever concrete evidence we have.Impact
replies=2shape.testerandconfig) in the same Feishu DM in the same hour, with two different trigger shapes. We do not yet have a controlled reproduction script.What we have not yet verified
lastToolError/mutatingActionbehavior occurs on other channels (Discord / Telegram / WebChat).execstdout heuristic or by a different code path. We have not yet pulled the trajectory for that run.configuration fileor something else about the exec metadata (run # → print text).We can run additional bisects or supply more trajectory files if maintainers want to narrow this down.
Related issues (already triaged, none matched our case)
blockStreamingDefault: "on"drops final reply): different mechanism —replies=0, no warning card. Not the same bug.open_message_idshape mismatch, different code path.update_multi): JSON 2.0 schema config, fixed in earlier versions.channels.feishu.streaming=false(final message may show only tail / truncated) #10078 (streaming=true even whenstreaming=false): config-respected, different layer.surface nested embedding fetch failures): CLI-level cause chain formatting, not gateway-runtime. Different code path.Reporter
~/.openclaw/agents/<agent>/sessions/.~/Library/Logs/openclaw/gateway.log.