Background Bash tasks hang forever on any stdin read (foreground does not)
Bug: background Bash tasks hang forever on any stdin read (foreground does not)
Claude Code: 2.1.209 (desktop app) · Platform: macOS 15 (Darwin 25.5.0) · Shell: zsh
Summary
A Bash tool call with run_in_background: true leaves child processes' stdin connected to
something that never reaches EOF. Any command that reads stdin (cat with no file operand,grep with no file, an interactive prompt, etc.) blocks forever.
The identical command in a foreground Bash call exits immediately, because there stdin is/dev/null. So the same script succeeds in the foreground and hangs indefinitely in the
background.
This is made much worse by three things:
- Background tasks appear to have no timeout. The tool schema documents
timeout (default 120000 ms, max 600000 ms), but that only applies to foreground calls.
My real-world instance ran for 17 h 45 m before I noticed it in the Background tasks panel.
- A blocked task is indistinguishable from a slow one. The output file just stays empty.
Reading it returns "file exists but contents are empty" — no hint that the process is
parked on a stdin read.
- The completion notification never fires, so the model waits for a result that will never
arrive, and reports to the user that work is "still running" when it is permanently wedged.
Reproduction
Foreground (run_in_background omitted) — works:
echo "FG STEP1"; cat >> /dev/null; echo "FG STEP2 reached"
FG STEP1
FG STEP2 reached
Background (run_in_background: true) — hangs indefinitely:
echo "STEP1"; grep -n "no-such-string" probe.txt || cat >> /dev/null; echo "STEP2 reached"
Output file contents, unchanged until the task is killed with TaskStop:
STEP1
STEP2 is never reached. Same result with a bare cat >> /dev/null.
Odd detail that may point at the cause
Inside a background task, the shell's own stdin appears to be /dev/null:
[ -t 0 ] && echo TTY || echo "NOT a tty" # -> NOT a tty
ls -l /dev/fd/0 # -> cr--r--r-- root wheel 0x3000002
read -t 5 line; echo $? # -> returns 1 (EOF) immediately, does NOT time out
So a bash builtin reading fd 0 sees EOF right away, yet a spawned cat inheriting that same fd 0
blocks forever. Whatever fd children actually receive is not the /dev/null the shell reports.
Impact
The failure mode is silent and expensive. In my case an agent workflow issued:
grep -n "private func requestDismiss" SomeFile.swift || cat >> /dev/null; python3 - << 'EOF' ... EOF; xcodebuild ...
The || cat fallback was a poorly written no-op (|| true was intended) — but in the foreground
it would have been harmless, and the intended python edit + build would have run. In the
background it wedged at the first command, so the file edit and build never happened, the task
sat "Running" for 17+ hours, and the model reported a build as in-progress that had never started.
Suggested fixes (any one of these would have prevented it)
- Redirect child stdin from
/dev/nullfor background tasks, matching foreground behavior.
(Most direct fix — the two modes should not differ here.)
- Apply a timeout to background tasks too, or expose
timeoutfor them, with a sane ceiling. - Surface blocked-on-read state: if a background task has produced no output for N minutes and
its process is in an uninterruptible/blocked read, say so in the task panel and in the result
the model sees when it reads the output file.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗