Bash tool: unrelated stale check.py/pytest output bleeds into tool_result for `for` loops with command substitution
Description
While running a for loop containing command substitution ($(basename "$f")) inside a single Bash tool call, the tool result repeatedly returned output that appears to come from a completely unrelated scripts/dev/check.py invocation (a project-specific pytest+lint gate script) in the same repo — not from the command that was actually run.
Reproduction
Repo has a PreToolUse hook in .claude/settings.json scoped narrowly to git commit:
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git commit *)",
"command": "scripts/dev/hooks/pre_commit_gate.sh",
"timeout": 480
}
]
}
]
}
None of the commands below matched git commit, yet the same stale-looking output appeared repeatedly:
for f in "$TEST_DIR"/.claude/hooks/*.sh; do
bash -n "$f" && echo "OK: $(basename "$f")" || echo "FAIL: $(basename "$f")"
done
Tool result (observed multiple times, nearly identical text, duration varying by <1s each time):
scripts/dev/check.py falhou:
→ test/test_implementations_new.py::test_endpoint_timeout
→ .../starlette/testclient.py:1: StarletteDeprecationWarning...
→ 28 passed, 19 warnings in 363.xx s (0:06:03)
2 gate(s) falharam.
What I checked
ps auxshowed no runningpytest/check.py/python3/curlprocess at the time of failure — ruling out an actually-hung process.- Trivial commands (
echo,cd ... && pwd,ps aux) run in between always succeeded cleanly. - Re-running the exact same
for-loop command was non-deterministic: sometimes it failed with this message, sometimes it returned the correct output. - Splitting the loop into individual
bash -n <file>calls (one Bash tool invocation per file, no loop, no command substitution) never reproduced the issue — 10/10 clean runs. - No matching hook in
.claude/settings.local.jsonor global~/.claude/settings.jsonthat could explain it (checked — noPreToolUsehooks there at all).
Environment
- Claude Code CLI, macOS (Darwin 25.5.0)
- Repo has a
PreToolUsehook onBashscoped via anifcondition (see above) that runs a ~6 minute pytest suite beforegit commit
Hypothesis (unconfirmed)
Looks like output/notification cross-talk in the tool-execution layer — possibly a stale or concurrently-running background job's result (from an unrelated check.py invocation, maybe from this same session's hook infra or a race in how if-scoped PreToolUse hook results are attributed to tool calls) getting attached to an unrelated Bash tool_result, more likely to surface when the command itself has more internal steps (loop + subshell command substitution) than a single-line command.
Impact
Not data-destructive, but wastes significant time chasing what looks like a real failure/hang, and erodes trust in tool_result output during a session.