-
-
Notifications
You must be signed in to change notification settings - Fork 80.7k
[Bug]: exec tool treats subprocess nonzero exit codes as tool failures, masking stdout and exit-code semantics #97318
Copy link
Copy link
Open
Labels
P2Normal backlog priority with limited blast radius.Normal backlog priority with limited blast radius.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.ClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.ClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.Channel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.Session, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.
Description
Metadata
Metadata
Assignees
Labels
P2Normal backlog priority with limited blast radius.Normal backlog priority with limited blast radius.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.ClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.ClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.Channel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.Session, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.
Type
Fields
Priority
None yet
Summary
The
exectool raises a tool failure at the framework level whenever the spawned subprocess exits with a nonzero code. This prevents the agent from receiving stdout, stderr, or the exit code as a normal return value. As a result, scripts that use exit codes semantically (a standard Unix convention — e.g.0= ok,1= condition X,2= condition Y) cannot be used as-is in any tool-driven workflow, including cron payloads.Environment
2026.6.10execagentTurnruns, but the underlying behavior is the exec tool itself.Concrete failure mode
The cron job
daily-cost-auditrunspython3 daily-cost-audit.py. The script intentionally exits1when daily spend exceeds a threshold:0→ spend OK, no action1→ "alert needed", stdout contains the report to postThe cron prompt was written to branch on exit code:
What actually happens when the script exits
1:exectool call errors out at the framework level before returning to the agentcron action=runsshowslastError: ⚠️ 🛠️ \run python3 …` failed`consecutiveErrorsincrementsFrom the agent's perspective the tool simply failed and there is nothing to act on.
Why this is a problem
Exit codes carrying semantic meaning is one of the oldest and most widely used Unix conventions. Treating any nonzero exit as a tool failure:
grep, which returns1on "no match", a successful no-result outcome)Reproduction
/tmp/exit1.py:toolsAllow: ["exec"], callexecwithcommand: "python3 /tmp/exit1.py"."threshold exceeded"is not returned to the agent's context.Workaround
Wrap the call so the shell always exits
0to the exec tool, encoding the real exit code in stdout:The agent then parses the
---EXITCODE:N---line. This works but is non-obvious, must be applied everywhere, and silently inverts a long-standing Unix convention.Suggested fix
Any one of these would address the issue:
A. Treat subprocess exits in the range 0–127 as normal returns (the agent gets stdout/stderr + exit code). Only raise a tool failure for actual signals (128+, e.g. SIGKILL/137, SIGTERM/143) or
execitself being unable to spawn the process.B. Add an
exectool option such asallowNonzeroExit: true(oracceptedExitCodes: [0,1,2]) that, when set, makes the tool return{ stdout, stderr, exitCode }to the agent instead of raising.C. At minimum, document the current behavior explicitly with the wrapper pattern above, so users do not lose hours debugging a script that "works in the shell but fails as a tool call."
Impact
Found via a
daily-cost-auditcron that had been silently failing to post the daily cost-alert. The misdiagnosis cost ~45 minutes (the framework error message also made the cause hard to spot — filed separately).Related
toolsAllow/lightContext) — being filed separately. The two interact in cron contexts.exectool failure message format — being filed separately.