[BUG] `claude -p` process doesn't terminate after agent session completes (macOS)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When using claude -p as a subprocess within an orchestration pipeline, the process completes its agent session (reads files, writes output files, generates final response) but never exits. The parent process blocks indefinitely waiting for the subprocess to terminate.
This was observed during a real-world orchestration scenario: a script pipes a prompt into claude -p --model opus --allowedTools "Read,Write,Glob,Grep,Bash(ls *)" and captures the output. The Claude agent ran for some time, successfully completed all its work (confirmed by inspecting the Claude Code session), wrote output files via the Write tool, and produced a final summary response. However, the claude -p process never terminated, leaving the parent pipeline hung.
What Should Happen?
After the agent session completes and the final response is generated, claude -p should flush stdout and exit with code 0.
Actual Behavior
The agent session completes (visible in Claude Code session history), output files are written, a final response is generated, but:
- The process does not exit
- stdout is not closed
- The parent pipeline blocks indefinitely waiting for EOF/process exit
- Must be manually killed
Steps to Reproduce
- Create a prompt that instructs the agent to read multiple files, analyze them, and write output files using the Write tool
- Pipe the prompt into
claude -pwith tools enabled:
echo "Read /path/to/data.json, analyze the contents, then write a summary to /tmp/summary.json and /tmp/summary.md" | claude -p --model opus --allowedTools "Read,Write,Glob,Grep"
- Wait for the agent to complete its work (visible in
claudesession list or by checking if output files were written) - Observe that the process does not terminate despite the session being complete
The issue is more reliably triggered with:
- Longer sessions (many tool calls over extended time)
- Opus model (slower, longer sessions)
- Write tool usage in the agent's final actions
- Piped input (prompt via stdin rather than argument)
Claude Model
Not sure / Multiple models
Is this a regression?
Yes, this worked in a previous version
Last Working Version
unsure
Claude Code Version
2.1.19
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Context
This was discovered while running automated QA orchestration where my orchestration script spawns Claude agents as subprocesses via claude -p. The issue has been observed across multiple phases of the pipeline using both Sonnet and Opus models. The most recent instance involved an Opus session that ran for approximately one hour of tool calls and hung after the agent finished.
The agent's final actions in the session (confirmed via Claude Code session viewer):
Write(/path/to/trend-report.json) — 397 lines written successfully
Write(/path/to/trend-report.md) — 222 lines written successfully
"Both files are written. Here's a summary: ..." — final response generated
After this, the claude -p process remained alive indefinitely. The parent process was stuck waiting for process exit/stdout EOF.
Related Issues
- #20084 — Identical symptoms on Windows (process completes but doesn't terminate in
-pmode) - #9026 — Similar hang in
-pmode without TTY (closed, older version) - #16487 — Process tree cleanup issues with piped output on macOS
- #15918 — Zombie process with
--resumeand-p
Workaround
Marker file polling: Instead of waiting for claude -p to exit and reading stdout, instruct the agent to write a marker file as its final action, then poll for that file:
# Launch claude -p in the background
claude -p --model opus --allowedTools "Read,Write,Glob,Grep" < "$PROMPT_FILE" > /dev/null 2>&1 &
CLAUDE_PID=$!
# Poll for the marker file the agent was told to write as its last action
while ! test -f "$MARKER_FILE"; do
sleep 5
if ! kill -0 $CLAUDE_PID 2>/dev/null; then break; fi
done
# Give the agent a moment to flush any remaining writes, then kill the hung process
sleep 15
kill $CLAUDE_PID 2>/dev/null
wait $CLAUDE_PID 2>/dev/null
# Read output from the marker file instead of stdout
cat "$MARKER_FILE"
The prompt includes an instruction like: "When you have completed ALL your work, as your very last action, write the text 'done' to /path/to/marker-file."
This sidesteps the hang entirely — it doesn't rely on process exit or stdout EOF, using the filesystem as a completion signal instead. The tradeoff is you lose stdout output and must have the agent write its results to files.
This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗