macOS: Read of an image >~200KB deadlocks every subsequent API call (surfaces as ECONNRESET after retry ladder); session permanently poisoned incl. --resume

Status Open
Reported on v2.1.225
Maintainer reply None cached
Activity 2 comments · opened Aug 11, 2026

Summary

On macOS (arm64, macOS 26.3), when the built-in Read tool ingests an image whose raw size is above ~200KB (base64 ~256KiB), the next API request never completes: the CLI silently retries for ~5.5 minutes (the full 10-step ladder), then reports API Error: Unable to connect to API (ECONNRESET) and exits 1. The image block persists in the session transcript, so every subsequent call — including any --resume of that session — fails identically. In our case a headless daemon that resumes one long-lived session was down for 17 hours from a single 411KB image.

The server is not involved: the identical payload succeeds instantly via curl from the same machine, both auth paths.

One-command repro (macOS)

Any JPEG/PNG ≥ ~210KB:

printf '%s' 'Use the Read tool on /tmp/big.jpg then output one word.' | claude -p --model claude-haiku-4-5-20251001 --allowedTools "Read"
# hangs silently ~5.5 min → "API Error: Unable to connect to API (ECONNRESET)", exit 1

Isolation matrix (all on the same machine/token unless noted)

| Probe | Result |
|---|---|
| image ≤166KB raw (128/512/768/896px variants) | works |
| image ≥206KB raw (1024px, 206KB) | fails |
| threshold bracket | fail cliff between 166KB and 206KB raw ≈ [221KiB, 275KiB] base64 — consistent with a 256KiB constant |
| model | fails on both claude-opus-4-5-20251101 and claude-haiku-4-5-20251001 |
| CLI version | fails on 2.1.225 and 2.1.226 (native builds) |
| auth | fails with subscription OAuth and ANTHROPIC_API_KEY |
| sandbox | fails with sandbox enabled and {"sandbox":{"enabled":false}} |
| fresh session vs resumed | fails in a fresh session (not a big-context issue; session was 3 messages) |
| 600KB compressible text via Read | works |
| 300KB incompressible (base64 noise) text via Read | works — rules out wire-size / compression theories; trigger is image content blocks specifically |
| same 206KB image via curl to /v1/messages, IPv4, API key | HTTP 200 in 0.9s |
| same image via curl, IPv6 | 200 in 0.9s |
| same image via curl with subscription bearer + anthropic-beta: oauth-2025-04-20 | 200 in 1.3s |
| same image, same CLI version, Linux | works |

Process state during the hang (sample on macOS)

  • The process accumulates 8+ ESTABLISHED TCP connections to api.anthropic.com:443 (one per retry; none ever completes a request; no data flowing).
  • A bundled native module (.99cedbef9bde5bbb-0.node, Rust symbols, plus tokio-rt-worker threads and an HTTP Client thread) shows threads parked in _pthread_cond_wait / __psynch_cvwait throughout the hang — consistent with a deadlock in a native image-handling or dispatch path rather than a network failure. Full sample output available on request.

Why this is worse than a failed call

The oversized image is written into the session JSONL before the failure, so the session is permanently poisoned: every later turn and every --resume inherits it (same shape as #8202/#26126, but with a transport-level symptom no HTTP status ever reaches). The 2.1.126 self-healing ("oversized images in history are automatically removed and the request retried") does not fire — this image violates no documented limit (1408×768, 411KB, valid JPEG), and the failure never surfaces as a 4xx.

Suggested fixes

  1. Find/fix the macOS-specific deadlock on large image blocks (the cond_wait stacks in the bundled native module during dispatch are the lead).
  2. Extend the 2.1.126 auto-strip to trigger on repeated transport-level failures of a request containing image blocks — after N identical resets, drop the newest image and retry, so one bad image can't strand a session (critical for headless/daemon usage).
  3. Read should derive media_type from magic bytes, not extension (#13396) — related hygiene in the same path (our trigger file was JPEG bytes named .png).

Environment

  • Claude Code 2.1.225 and 2.1.226 (native install), macOS 26.3 (25D125), arm64 (Mac Mini)
  • Reproduced headless (-p) with --allowedTools Read; unaffected Linux control: CLI 2.1.227 on x86_64

Related: #51164, #56140, #8202, #26126, #13396, #30124

🤖 Filed with Claude Code after a joint human/Claude root-cause session.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗