[BUG] Session Stops Silently Mid-Turn with `stop_reason: null`
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?
Claude Code session stopped silently mid-turn without completing its response. The model was actively working on a task (debugging a failed crontab command), produced a thinking block ("Something failed. Let me debug this step by step."), but then stopped without outputting any tool calls, text, or error messages.
The session transcript shows:
- All 34 assistant entries have
stop_reason: null- even responses that appear complete. This is abnormal; completed responses should havestop_reason: "end_turn"or"tool_use". - The truncated response (entry 66) contains only a single thinking block with
output_tokens: 1 - Stop hooks ran as if the turn was complete (
stop_hook_summaryrecorded) - User experienced 22 seconds of silence before manually intervening
What Should Happen?
- After thinking "Something failed. Let me debug this step by step.", Claude should have continued with tool calls to actually debug the issue
- If the API response was incomplete or interrupted, Claude Code should show an error or retry
stop_reasonshould reflect the actual completion state (end_turn,tool_use,max_tokens, etc.) - notnullon every response
Error Messages/Logs
No error messages were displayed. The session simply stopped without any indication.
**Session transcript analysis (via JQ queries on session JSONL):**
# Entry 66 - The truncated response
{
"type": "assistant",
"ts": "2026-01-23T02:31:56.013Z",
"model": "claude-opus-4-5-20251101",
"stop_reason": null,
"stop_sequence": null,
"usage": {
"input_tokens": 8,
"output_tokens": 1,
"cache_read_input_tokens": 54753
},
"content_count": 1,
"content_types": ["thinking"]
}
# Content was just:
[{"type": "thinking", "thinking": "Something failed. Let me debug this step by step."}]
# No tool_use or text blocks followed - response was truncated
**Stop hook summary (ran as if turn was complete):**
{
"subtype": "stop_hook_summary",
"hookCount": 3,
"stopReason": "",
"preventedContinuation": false
}
Steps to Reproduce
The exact trigger is unclear, but based on analysis:
- Start a Claude Code session with Opus 4.5
- Have Claude execute a multi-step task with
bypassPermissionsmode - During the task, have a tool return an error (e.g., bash command exits with code 1)
- Observe if the response stream completes properly after Claude's thinking block
Session context that may be relevant:
- Permission mode:
bypassPermissions - Working in a local project directory
- Task: Installing a crontab entry (Phase 2 of a 4-phase plan)
- Trigger: Bash command
crontab -l | ...returned exit code 1
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.17
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Session Details
| Field | Value |
|-------|-------|
| Session ID | 2de75cd5-945d-4367-a889-b81354e91a00 |
| Session Path | ~/.claude/projects/<project>/2de75cd5-945d-4367-a889-b81354e91a00.jsonl |
| Duration | ~2 minutes (02:30:36 - 02:32:23 UTC) |
| Total Entries | 238 |
| Assistant Entries | 34 (all have stop_reason: null) |
| Errors | 3 (2x "Exit code 1", 1x "Hook PreToolUse:Bash denied") |
Key Observations
stop_reason: nullon ALL responses: Every assistant entry in the session hasstop_reason: null. This suggests Claude Code may not be capturing the final streaming chunk's stop_reason correctly.
- Low output token counts: Output tokens range from 1-5 across all responses, suggesting these are streaming progress entries rather than complete responses.
- Response truncation pattern:
- Tool result received (exit code 1)
- Claude produces thinking block only
- No follow-up action
- Hooks run as if turn complete
- Silence
- Timeline of the bug:
````
02:31:53.xxx - Bash tool_use executed
02:31:53.xxx - Tool result: "Exit code 1" (is_error: true)
02:31:56.013 - Thinking: "Something failed. Let me debug..."
02:31:56.648 - stop_hook_summary runs (stopReason: "")
[22 seconds of silence]
02:32:18.xxx - User intervenes: "what is your session id"
Hypothesis
Claude Code appears to treat streaming responses as complete before the actual stop_reason is received. When a response contains only a thinking block (possibly due to API stream interruption), Claude Code runs stop hooks and waits for user input instead of:
- Recognizing the response is incomplete
- Retrying or showing an error
- Properly capturing the final stop_reason
JQ Queries Used for Analysis
SESSION_FILE="/path/to/session.jsonl"
# Check stop_reason distribution
jq -s '[.[] | select(.type == "assistant") | .message.stop_reason] | group_by(.) | map({reason: (.[0] // "null"), count: length})[]' "$SESSION_FILE"
# Find truncated entry
sed -n '66p' "$SESSION_FILE" | jq '.message'
# Verify all responses have stop_reason: null
jq -c 'select(.type == "assistant") | {ts: .timestamp[11:19], stop: .message.stop_reason}' "$SESSION_FILE"This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗