CLI freezes with "No messages returned" error - never recovers
When running Claude Code CLI in a long-running automated script, I occasionally encounter a "No messages returned" error that causes the CLI to freeze indefinitely. The process never completes or exits - it just hangs.
Context
I'm building an autonomous coding agent that uses Claude Code to implement features from a PRD (Product Requirements Document). I have a bash script (affectionately named "Ralph Wiggum") that iterates over user stories, having Claude implement them one at a time.
Command Being Run
cat "$SCRIPT_DIR/prompt.md" | claude --dangerously-skip-permissions --verbose --print 2>&1 | tee "$TEMP_OUTPUT"
The prompt file contains instructions for Claude to:
- Read a JSON-based PRD with user stories
- Pick the highest priority incomplete story
- Implement it following existing codebase patterns
- Run tests and linting
- Commit the changes
Error Output
we didn't catch the error
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:
Error: No messages returned
at GO9 (file:///opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js:5390:73)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
Observed Behavior
- Claude begins processing the prompt normally
- At some point during execution, the error above is printed to stderr
- The process does not exit - it hangs indefinitely
- No further output is produced
- Ctrl+C is required to terminate the process
Expected Behavior
- The CLI should either recover from this transient error and continue, or
- Exit with a non-zero status code so the calling script can retry
Workaround Attempted
I've implemented retry logic in my bash script that:
- Captures output to a temp file
- Greps for "No messages returned"
- Retries up to 3 times with 5-second delays
However, this doesn't help because the CLI freezes - it doesn't exit, so the script can't detect completion and retry. The only option is to manually Ctrl+C.
Reproduction
This is intermittent and hard to reproduce on demand. It seems to occur:
- During long-running sessions with many tool calls
- More frequently when iterating over multiple tasks
- Possibly related to rate limiting or API timeouts?
Environment
| Component | Version |
|-----------|---------|
| Claude Code | 2.1.12 |
| macOS | 26.2 (Build 25C56) |
| Darwin Kernel | 25.2.0 (ARM64) |
| Node.js | v23.11.0 |
| npm | 10.9.2 |
| Architecture | Apple Silicon (arm64, M1) |
Minimal Reproduction Script
#!/bin/bash
# "Ralph Wiggum" - iterates over specs to implement features
MAX_ITERATIONS=10
TEMP_OUTPUT=$(mktemp)
trap "rm -f $TEMP_OUTPUT" EXIT
for i in $(seq 1 $MAX_ITERATIONS); do
echo "Iteration $i"
# This command occasionally triggers the freeze
cat prompt.md | claude --dangerously-skip-permissions --verbose --print 2>&1 | tee "$TEMP_OUTPUT" || true
# Check for completion (never reached when frozen)
if grep -q "COMPLETE" "$TEMP_OUTPUT"; then
echo "Done!"
exit 0
fi
done
Suggested Fix
The unhandled promise rejection at cli.js:5390 should be caught and either:
- Retried with exponential backoff internally
- Result in a clean exit with a specific error code (e.g., exit 2 for "retry recommended")
- At minimum, not freeze the entire process
Additional Context
The error message "we didn't catch the error" suggests this is a known gap in error handling. The stack trace points to GO9 function in cli.js:5390 which is likely minified code, but the issue is clearly an unhandled async rejection.
11 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
I'm having the same issues but I'm not using --resume flag. The 3 possible duplicates suggested are not the same case.
Exactly same problem:
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:
Error: No messages returned
at gAf (B:/~BUN/root/claude.exe:5329:78)
at processTicksAndRejections (native:7:39)
I'm using:
while ($true) { $env:IS_SANDBOX="1"; $prompt = Get-Content ralph/prompt.md -Raw; claude $prompt --dangerously-skip-permissions --chrome --print --no-session-persistence; Start-Sleep -Seconds 2 }
And only happen on specific md file which is very hard to find out what is that!
Workaround: Two-part solution for reliable completion detection
We ran into this exact issue building an autonomous "Ralph Wiggum" agent loop . After much debugging, we found a reliable workaround with two parts. It's more complex than a script like this should need to be, but it's robust and we've been running it in production without any issues. We plan to simplify once this bug is fixed in Claude Code.
Part 1: Use
--output-format stream-jsonto detect completionThe root cause is that Claude completes its work successfully, but hangs during exit/cleanup. The key insight: the "type":"result" JSON message is emitted BEFORE the hang occurs.
# Instead of:
cat prompt.md | claude --dangerously-skip-permissions --print 2>&1 | tee "$TEMP_OUTPUT"# Use:
```
PROMPT="$(<prompt.md)"
claude --dangerously-skip-permissions -p "$PROMPT" --output-format stream-json --verbose 2>&1 &
CLAUDE_PID=$!
while IFS= read -r line; do
echo "$line" >> "$TEMP_OUTPUT"
if [[ "$line" == '"type":"result"' ]]; then
RESULT_RECEIVED=true
# Session complete - give 2s to exit, then kill if hung
( sleep 2; kill $CLAUDE_PID 2>/dev/null ) &
KILLER_PID=$!
break
fi
done < <(cat /proc/$CLAUDE_PID/fd/1 2>/dev/null)
wait $CLAUDE_PID 2>/dev/null
kill $KILLER_PID 2>/dev/null
# Extract the actual result text using jq
RESULT_TEXT=$(grep '"type":"result"' "$TEMP_OUTPUT" | jq -r '.result // empty' 2>/dev/null | head -1)
echo "$RESULT_TEXT"
if grep -q "<promise>COMPLETE</promise>" "$TEMP_OUTPUT" 2>/dev/null; then
echo "All tasks completed!"
exit 0
fi
if grep -q "<promise>END_OF_STORY</promise>" "$TEMP_OUTPUT" 2>/dev/null; then
echo "✓ Iteration completed successfully"
fi
We've been running this in production for days zero freezes. Before this we had problems every other story claude code was finishing. The combination of stream-json detection + graceful kill + explicit completion signals makes it bulletproof.
Hope this helps others hitting the same issue!
This is Gold! thanks so much for your detailed help above .. the equivalent to run on windows (powershell):
.\ralph\run-claude.ps1 -PromptFile "ralph/prompt.md" -Model "sonnet"
I had the same error, but it did not have the same root cause. The agent had not finished running when it encountered the "No messages returned" error and hung. Here is the summary of the bug that I saw:
As a workaround, I implemented similar fixes to @minovap, but I also handled resuming an incomplete task. It seems to be working well.
I have the same problem which makes claud cli (sdk) useless.
I haven't investigated it but it seems there is a 10 minutes timeout for 'claude -p' and if the prompt takes more than 10 minutes it stops with that error.
It could be something else but the error/hanging happens around the 10 minutes mark.
Same problem here. Please fix.
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.