Silent stream abort causes Claude to stop mid-task without error
Description
When Claude Code is mid-task (executing a multi-turn agentic loop with tool calls), a transient interruption to the Anthropic API SSE stream causes Claude to silently stop working and return to the input prompt — with no error message, no retry, and no indication that anything went wrong.
The user comes back hours later expecting the task to be complete, only to find Claude sitting at the ❯ prompt as if it finished normally.
Environment
- Claude Code v2.1.83 (Bun single-file binary)
- Running inside tmux (for session persistence)
- Terminal access via Wetty (web terminal over SSH)
- Platform: ARM (NVIDIA Jetson), Linux 5.15
Steps to Reproduce
- Start a long-running agentic task (one that involves multiple tool calls in sequence)
- While Claude is mid-task (streaming an API response that includes both text and a
tool_useblock), the SSE stream gets interrupted (network hiccup, TCP reset, API timeout, etc.) - Claude displays whatever text it received before the interruption, then returns to the input prompt
Expected Behavior
Claude should detect that the stream ended abnormally (no message_stop event, or stop_reason: null) and either:
- Retry the API request for the interrupted turn
- Display an error message ("API stream interrupted, retrying..." or similar)
- At minimum, warn the user that the task was interrupted
Actual Behavior
Claude silently returns to the input prompt as if the turn completed normally. No error, no warning, no retry.
Root Cause (from source analysis)
The Anthropic SDK's SSE stream iterator silently swallows AbortError and FetchRequestCanceledException:
// In the SSE stream async iterator:
catch($) {
if (Ac($)) return; // ← silently ends the iterator
throw $;
}
function Ac(A) {
return typeof A === "object" && A !== null &&
(("name" in A) && A.name === "AbortError" ||
("message" in A) && String(A.message).includes("FetchRequestCanceledException"))
}
When the stream is aborted, the for await loop completes normally. Claude Code receives whatever content blocks arrived before the interruption (e.g., text) but not the blocks that hadn't arrived yet (e.g., tool_use). Without a tool_use block, the agentic loop ends and Claude returns to the prompt.
Impact
This is particularly problematic for:
- Long-running tasks (research pipelines, multi-file edits, iterative reviews)
- Headless/unattended operation (running in tmux, screen, or scheduled tasks)
- Users who step away expecting Claude to complete work autonomously
The silent failure means the user has no way to know the task didn't complete without manually checking.
Suggested Fix
In the main agent loop, after consuming the stream, check whether the response ended cleanly:
- Was a
message_stopSSE event received? - Is
stop_reasonnon-null? - If the response contains only text blocks but the model was clearly mid-thought (no
end_turn), treat it as an interrupted response and retry
At minimum, surface the interruption to the user rather than silently returning to the prompt.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗