Persistent ECONNRESET Errors on macOS Network Connections

Open 💬 48 comments Opened Aug 13, 2025 by ryanmr82

Bug Description
I keep getting the following errors, and it's causing connection errors and it's disconnecting tasks within Claude. This only happens on my Mac OS; it doesn't happen on my Windows server (which is on the same network) nor does it happen on my Linux server (which is on an OCI network). I've read online that this is an issue with Mac OS. I have been troubleshooting all day for over 10 hours, and I can't figure out what's going on. I believe this is a Claude issue, not a user issue. If it is a user issue, please guide me in regards to how to resolve this issue because I am stumped. I have been working on this for over 12 hours now, and that made zero progress.

These are the errors that I see: n, pass verbose: true in the second argument to fetch()) (ECONNRESET)
⎿ API Error (Connection error.) · Retrying in 32 seconds… (attempt 7/10)
⎿ Error (The socket connection was closed unexpectedly. For more information, pass verbose: true in the second argument to fetch()) (ECONNRESET)
⎿ API Error (Connection error.) · Retrying in 37 seconds… (attempt 8/10)
⎿ Error (The socket connection was closed unexpectedly. For more information, pass verbose: true in the second argument to fetch()) (ECONNRESET)
⎿ API Error (Connection error.) · Retrying in 33 seconds… (attempt 9/10)
⎿ Error (The socket connection was closed unexpectedly. For more information, pass verbose: true in the second argument to fetch()) (ECONNRESET)
⎿ API Error (Connection error.) · Retrying in 40 seconds… (attempt 10/10)
⎿ Error (The socket connection was closed unexpectedly. For more information, pass verbose: true in the second argument to fetch()) (ECONNRESET)
⎿  API Error: Connection error.

Environment Info

  • Platform: darwin
  • Terminal: Apple_Terminal
  • Version: 1.0.77
  • Feedback ID: 64d3964f-f289-4803-a28c-1fd74ede068e

Errors

[{"error":"Error\n    at <anonymous> (unknown)\n    at onData (node:child_process:153:110)\n    at emit (node:events:95:22)\n    at addChunk (internal:streams/readable:265:47)\n    at readableAddChunkPushByteMode (internal:streams/readable:243:18)\n    at handleNumberResult (internal:streams/native-readable:69:18)\n    at <anonymous> (internal:streams/native-readable:43:68)\n    at processTicksAndRejections (unknown:7:39)","timestamp":"2025-08-13T09:22:33.750Z"},{"error":"Error: Connection error.\n    at new eW (unknown:1:28)\n    at new qY (/$bunfs/root/claude:788:1853)\n    at new BM (/$bunfs/root/claude:788:2749)\n    at makeRequest (/$bunfs/root/claude:805:4449)\n    at processTicksAndRejections (native:7:39)","timestamp":"2025-08-13T09:32:49.094Z"}]

View original on GitHub ↗

48 Comments

github-actions[bot] · 11 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/5585
  2. https://github.com/anthropics/claude-code/issues/4297
  3. https://github.com/anthropics/claude-code/issues/3131

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

ryanmr82 · 11 months ago

I'm not sure if this is a duplicate because this is only occurring on macOS and some other people are seeing it on Windows. I think this requires further investigation.

yasserlens · 11 months ago

I'm getting connectivity issues across all apps (including Anthropic's Claude desktop app) and web requests (Chrome).
The issues go away when I shut down from Claude Code instances (running in a terminal inside Cursor)

I'm on an Apple M2 Pro
macOS
version: 15.6

MarceloCajueiro · 9 months ago

I'm having the same @yasserlens' error. Any thought?

yasserlens · 9 months ago

@MarceloCajueiro my issue is now resolved - it had nothing to do with the Claude app it seems. Turns out this problem I had was due to using a VPN (TunnelBear) which seemed to have had issues. Uninstalling and deleting it helped (although was a challenge - uninstalling it didn't remove all pieces of the software that interfered with the network so I had to do some stuff manually). All done with the help of ChatGPT/Claude :)

MarceloCajueiro · 9 months ago

Thanks for the light @yasserlens! Here is the diagnostic and probably solution.

🔍 Additional Root Cause: VPN Residue Causing ECONNRESET on macOS

I experienced similar ECONNRESET issues on macOS after uninstalling a VPN client. Through investigation with Claude Code, we discovered leftover VPN network configurations were interfering with socket connections.

Diagnostic Findings

System: macOS 15.0.0 (Darwin 25.0.0)

Symptoms:

  1. 6 active utun tunnel interfaces (normal: 2-3)
  2. Multiple conflicting IPv6 default routes:

``
default → fe80::%utun0
default → fe80::%utun1
default → fe80::%utun2
default → fe80::%utun3
default → fe80::%utun4
default → fe80::%utun5
``

  1. Network performance degraded: Latency spiked 196% (26ms → 77ms)
  2. Unstable routing: Traceroute showing timeouts and packet loss

How This Causes ECONNRESET

The multiple competing IPv6 routes cause routing conflicts where:

  • TCP connections start on one interface
  • Packets get routed through different utun interfaces mid-stream
  • Connection appears broken → socket reset → ECONNRESET

This explains why it's macOS-specific and intermittent (depends on which route the OS chooses).

Diagnostic Commands

# Check for excessive utun interfaces
ifconfig | grep -c "utun"

# Check for multiple IPv6 default routes
netstat -rn | grep "default.*utun"

# Test if VPN residue exists
scutil --nc list

If you see 4+ utun interfaces and multiple IPv6 default routes, VPN residue is likely interfering.

✅ Solution That Worked

Disable IPv6 on Wi-Fi:

sudo networksetup -setv6off Wi-Fi

Results:

  • ECONNRESET issues resolved
  • Latency improved 48% (77ms → 40ms)
  • Stable routing restored

To re-enable: sudo networksetup -setv6automatic Wi-Fi

Why This Happens

After VPN uninstallation, macOS system services (nesessionmanager, rapportd) don't properly clean up tunnel interfaces. Research from Apple Developer Forums (2024-2025) confirms:

  • utun interfaces cannot be manually destroyed (ifconfig destroy fails)
  • Deleting routes doesn't work (auto-recreated)
  • Only reboot temporarily clears them (they return)

Alternative Solutions

If you need IPv6:

  1. Disable Handoff/Continuity (reduces utun creation):
  • System Settings → General → AirDrop & Handoff
  • Uncheck "Allow Handoff between this Mac and your iCloud devices"
  1. Monitor and reboot periodically:

``bash
# Check utun count
netstat -rn | grep "default.*utun" | wc -l
# If >3, reboot
``

Feature Request

Could Claude Code detect VPN residue during connection errors and suggest network cleanup? Something like:

⚠️  Detected network configuration issues (6 utun interfaces, IPv6 routing conflicts)
   This may cause ECONNRESET errors. Run diagnostics? [Y/n]

References

---

This solution might help others experiencing ECONNRESET on macOS after using VPN software. The key indicator is multiple utun interfaces with competing routes.

github-actions[bot] · 7 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

dacort · 6 months ago

This turned out to also be a network configuration error for me, but specifically with my MTU setting (and possibly eero secure).

When looking at my primary wireless interface (en0), I had an mtu of 1500:

 ifconfig | grep mtu
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500

Setting my MTU to 1492 (I had tried just 1400 originally) made everything work fine again.

sudo networksetup -setMTU Wi-Fi 1492

One thing that pointed me _away_ from it just being a CC issue was that I also couldn't load https://docs.bsky.app or https://superuser.com with Chrome or Vivaldi (but Safari worked fine). They're all behind a CloudFlare CDN so I don't know if they're implementing some TLS 1.3 thing in a specific way or what.

brigleb · 6 months ago

OK after tons of troubleshooting, it turns out the presence of Tailscale on my Mac, which I forgot I had installed and which was periodically launching, was my problem. This was causing intermittent failures, only on certain network stacks, that was driving me insane. Wanted to post this here to help others who might have this issue. Check your network and VPN setup!!

Update: More specifically, Tailscale (I suspect) set my TCP MSS (max segment size) to 512. Insane. Should be 1460, and presumably many apps expect that value. As Claude reports:

A TCP MSS of 512 is absurdly low—it's fragmenting your packets into tiny pieces, which would absolutely cause issues with HTTP/2 streaming while basic HTTP requests (like curl) might still work.

You can temporarily fix this on your Mac with sudo sysctl -w net.inet.tcp.mssdflt=1460 but that does not persist after reboot. I found it very difficult to remove residual Tailscale daemons, they stayed even after it was completely removed from System Settings. But maybe by adjusting that setting you can use Claude Code to track it down, which is what I did!

andreafalzetti · 6 months ago

We are experiencing the same error, and none of the above tips have worked. Please help!

S0uffle · 5 months ago

The issue with me is the Tidal Exclusive Mode. I don't know much about technical, so here is what I found :

TIDAL (and some other apps) installs a DNS proxy on your Mac. This intercepts all DNS lookups (converting domain names like api.anthropic.com to IP addresses) and routes them through TIDAL's system — likely for:

Bypassing regional restrictions on music
Optimizing streaming performance
Analytics/tracking

Use Tidal without Exclusive Mode solve this with me.

linchun3 · 5 months ago

FWIW: This happened to me and after some debugging, it was Lulu/little snitch that was blocking claude 😄

lukehebb · 5 months ago

Facing same issue. Without using a VPN I get this error constantly. Began happening randomly one day last week

Can't submit /feedback as it fails with or without VPN

Claude desktop app and web works with and without VPN, issue just affects claude code

CaptainMcCrank · 5 months ago

# ECONNRESET on macOS Despite All Network Connectivity Verified — Claude Code Internal Connection Handling Bug

Environment

| Component | Detail |
|:-:|:-:|
| OS | macOS (Apple Silicon / arm64) |
| Node | v22.17.0 (via nvm) |
| Claude Code | 2.1.30 (tested both native Bun install and npm Node install — identical failure) |
| Auth | OAuth, Max subscription |
| Working comparison | Same account/credentials work fine on Linux (Ubuntu, Claude Code 2.1.29) on the same network |

Summary

Claude Code 2.1.30 fails with ECONNRESET on every API call on macOS. Extensive troubleshooting has confirmed the issue is internal to Claude Code's connection handling, not the network, runtime, SDK, or auth.

What Works (all from the same Mac)

  • curl -v https://api.anthropic.com — TLS handshake, HTTP/2, returns 404 ✅
  • node -e "fetch('https://api.anthropic.com')..." — returns 404 ✅
  • node with https.request POST to /v1/messages — returns 401 ✅
  • node with http2 module to api.anthropic.com — returns 401 ✅
  • @anthropic-ai/sdk direct test (npm install @anthropic-ai/sdk) — returns 401 ✅
  • DNS resolution for api.anthropic.com, platform.claude.com, statsig.anthropic.com — all resolve ✅

⠀What Fails

  • claude — ECONNRESET within ~2 seconds, retries 10 times, never connects ❌
  • claude login — same ECONNRESET ❌
  • Both native (Bun) and npm (Node) installs fail identically ❌

⠀Verified Not the Cause

  • No VPN, proxy, firewall, or network content filters
  • No macOS system extensions (systemextensionsctl list returns 0)
  • No managed profiles (profiles list returns none)
  • /etc/hosts is clean (only localhost entries)
  • No HTTP_PROXY / HTTPS_PROXY / NODE_* environment variables set
  • macOS Application Firewall is disabled
  • No kernel extensions loaded (kextstat clean)
  • Credentials file (.credentials.json) is present with valid OAuth token
  • Removing and reinstalling Claude Code did not help
  • Clearing Keychain entries did not help

⠀Debug Log Evidence

2026-02-03T21:56:26.297Z [DEBUG] [API:request] Creating client, ANTHROPIC_CUSTOM_HEADERS present: false, has Authorization header: false

2026-02-03T21:56:26.297Z [DEBUG] [API:auth] OAuth token check starting

2026-02-03T21:56:26.297Z [DEBUG] [API:auth] OAuth token check complete

...

2026-02-03T21:56:27.374Z [ERROR] Error: Error: Connection error.

at makeRequest (/$bunfs/root/claude:361:4548)

at processTicksAndRejections (native:7:39)

Network-Level Observations

The connection does establish to 160.79.104.10:443 (confirmed via lsof) but then immediately resets. The lsof output shows the TCP socket transitions from ESTABLISHED to CLOSED state shortly after connecting:

node 11075 patrick 22u IPv4 0x2dc13daa605e3e7 0t0 TCP 192.168.x.x:50972->160.79.104.10:443 (CLOSED)

Notably, marketplace refresh succeeds within the same process:

2026-02-03T21:56:30.997Z [DEBUG] Successfully refreshed marketplace: claude-plugins-official

This suggests only specific API request paths are affected.

Telemetry Also Fails with the Same Error

[ERROR] Error: Error: 1P event logging: 26 events failed to export

(code=ECONNRESET, The socket connection was closed unexpectedly.

For more information, pass verbose: true in the second argument to fetch())

Suspected Cause

Something in Claude Code's internal HTTP client configuration or request construction is triggering a connection reset that does not occur when using the same SDK, same runtime, same credentials, and same endpoints outside of Claude Code. The fact that the marketplace refresh succeeds while API calls and telemetry fail within the same process suggests the issue may be specific to how certain requests are constructed or routed internally.

Steps to Reproduce

  1. Install Claude Code 2.1.30 on macOS (Apple Silicon)
  2. Authenticate via OAuth (Max subscription)
  3. Run claude from any directory
  4. Observe ECONNRESET within ~2 seconds, retrying up to 10 times before failing
CaptainMcCrank · 5 months ago
# # ECONNRESET on macOS Despite All Network Connectivity Verified — Claude Code Internal Connection Handling Bug # Environment Component Detail OS macOS (Apple Silicon / arm64) Node v22.17.0 (via nvm) Claude Code 2.1.30 (tested both native Bun install and npm Node install — identical failure) Auth OAuth, Max subscription Working comparison Same account/credentials work fine on Linux (Ubuntu, Claude Code 2.1.29) on the same network # Summary Claude Code 2.1.30 fails with ECONNRESET on every API call on macOS. Extensive troubleshooting has confirmed the issue is internal to Claude Code's connection handling, not the network, runtime, SDK, or auth. # What Works (all from the same Mac) curl -v https://api.anthropic.com — TLS handshake, HTTP/2, returns 404 ✅ node -e "fetch('https://api.anthropic.com')..." — returns 404 ✅ node with https.request POST to /v1/messages — returns 401 ✅ node with http2 module to api.anthropic.com — returns 401 ✅ @anthropic-ai/sdk direct test (npm install @anthropic-ai/sdk) — returns 401 ✅ DNS resolution for api.anthropic.com, platform.claude.com, statsig.anthropic.com — all resolve ✅ ⠀What Fails claude — ECONNRESET within ~2 seconds, retries 10 times, never connects ❌ claude login — same ECONNRESET ❌ Both native (Bun) and npm (Node) installs fail identically ❌ ⠀Verified Not the Cause No VPN, proxy, firewall, or network content filters No macOS system extensions (systemextensionsctl list returns 0) No managed profiles (profiles list returns none) /etc/hosts is clean (only localhost entries) No HTTP_PROXY / HTTPS_PROXY / NODE_ environment variables set macOS Application Firewall is disabled No kernel extensions loaded (kextstat clean) Credentials file (.credentials.json) is present with valid OAuth token Removing and reinstalling Claude Code did not help Clearing Keychain entries did not help ⠀Debug Log Evidence ### 2026-02-03T21:56:26.297Z [DEBUG] [API:request] Creating client, ANTHROPIC_CUSTOM_HEADERS present: false, has Authorization header: false ### 2026-02-03T21:56:26.297Z [DEBUG] [API:auth] OAuth token check starting ### 2026-02-03T21:56:26.297Z [DEBUG] [API:auth] OAuth token check complete ### ... ### 2026-02-03T21:56:27.374Z [ERROR] Error: Error: Connection error. ### at makeRequest (/$bunfs/root/claude:361:4548) ### at processTicksAndRejections (native:7:39) # Network-Level Observations The connection does establish to 160.79.104.10:443 (confirmed via lsof) but then immediately resets. The lsof output shows the TCP socket transitions from ESTABLISHED to CLOSED state shortly after connecting: ### node 11075 patrick 22u IPv4 0x2dc13daa605e3e7 0t0 TCP 192.168.x.x:50972->160.79.104.10:443 (CLOSED) Notably, marketplace refresh succeeds within the same process: ### 2026-02-03T21:56:30.997Z [DEBUG] Successfully refreshed marketplace: claude-plugins-official This suggests only specific API request paths are affected. # Telemetry Also Fails with the Same Error ### [ERROR] Error: Error: 1P event logging: 26 events failed to export ### (code=ECONNRESET, The socket connection was closed unexpectedly. ### For more information, pass verbose: true in the second argument to fetch()) # Suspected Cause Something in Claude Code's internal HTTP client configuration or request construction is triggering a connection reset that does not occur when using the same SDK, same runtime, same credentials, and same endpoints outside of Claude Code. The fact that the marketplace refresh succeeds while API calls and telemetry fail within the same process suggests the issue may be specific to how certain requests are constructed or routed internally. # Steps to Reproduce 1. Install Claude Code 2.1.30 on macOS (Apple Silicon) 2. Authenticate via OAuth (Max subscription) 3. Run claude from any directory 4. Observe ECONNRESET within ~2 seconds, retrying up to 10 times before failing

I was able to resolve this through a system reboot of all things. No clarity on why this worked.

aaronepinto-multiply · 5 months ago
OK after tons of troubleshooting, it turns out the presence of Tailscale on my Mac, which I forgot I had installed and which was periodically launching, was my problem. This was causing intermittent failures, only on certain network stacks, that was driving me insane. Wanted to post this here to help others who might have this issue. Check your network and VPN setup!! Update: More specifically, Tailscale (I suspect) set my TCP MSS (max segment size) to 512. Insane. Should be 1460, and presumably many apps expect that value. As Claude reports: > A TCP MSS of 512 is absurdly low—it's fragmenting your packets into tiny pieces, which would absolutely cause issues with HTTP/2 streaming while basic HTTP requests (like curl) might still work. You can temporarily fix this on your Mac with sudo sysctl -w net.inet.tcp.mssdflt=1460 but that does not persist after reboot. I found it very difficult to remove residual Tailscale daemons, they stayed even after it was _completely_ removed from System Settings. But maybe by adjusting that setting you can use Claude Code to track it down, which is what I did!

Thank you, this fixed it for me!

MarceloCajueiro · 5 months ago

Update: Found a deeper root cause beyond VPN/sysctl — Node.js HTTP/1.1 connection exhaustion

I commented here before about VPN residue and utun interfaces. That was real but turned out to be only part of the problem. After months of dealing with this intermittently, I finally spent a full day doing systematic testing and found the actual root cause — and it's something that affects every Node.js app on macOS, not just Claude Code.

The real problem

Node.js fetch (via undici) uses HTTP/1.1 by default, which opens one TCP connection per request. CDNs like Cloudflare limit concurrent TCP connections per IP to roughly 50-150. When any Node.js process on your machine exceeds that threshold, the server starts refusing connections with ECONNRESET/ECONNREFUSED.

Here's the critical part that explains why your browser stops working too: Cloudflare uses shared IPs. When your Node.js app floods 160.79.104.10:443 (Anthropic's Cloudflare-fronted API) with hundreds of HTTP/1.1 connections, Cloudflare can temporarily block your IP for all domains behind that CDN — GitHub, NPM, Discord, and thousands of other sites suddenly become unreachable from your browser.

curl and browsers don't have this problem because they use HTTP/2 by default, which multiplexes all requests over a single TCP connection.

Proof

I ran a controlled test battery against external APIs:

| Method | 200 concurrent requests | 300 concurrent |
|---|---|---|
| fetch (HTTP/1.1) | 4 ok, 196 fail | worse |
| https.get (HTTP/1.1) | 3 ok, 197 fail | worse |
| http2 (native) | 200 ok, 0 fail | 300 ok, 0 fail |
| fetch + allowH2 | 200 ok, 0 fail | 300 ok, 0 fail |
| localhost (any method) | 1000 ok, 0 fail | — |

Localhost never fails because there's no per-IP connection limit. The problem is exclusively with remote servers.

Why this is particularly bad on macOS

macOS has some TCP defaults that make this worse:

  • tcp.mssdflt = 512 (dial-up era value — several people in this thread found this, including @brigleb with Tailscale)
  • tcp.blackhole = 1 (silently drops packets instead of sending RST)
  • tcp.msl = 2000 (TIME_WAIT only 4 seconds — connections get recycled too aggressively)

These sysctl values compound the HTTP/1.1 connection flood problem. VPNs like Tailscale, TunnelBear, and Cloudflare WARP can also set mssdflt to 512, which fragments packets and makes HTTP/2 streaming fail even when the protocol negotiation works.

How I'm solving it (3 layers)

Layer 1: Enable HTTP/2 globally for all Node.js processes

# Install undici in a shared location
mkdir -p ~/.node && cd ~/.node
npm init -y && npm install undici

# Create preload script: ~/.node/enable-h2.cjs
cat > ~/.node/enable-h2.cjs << 'SCRIPT'
try {
  const { setGlobalDispatcher, Agent } = require(
    require('path').join(require('os').homedir(), '.node/node_modules/undici')
  );
  setGlobalDispatcher(new Agent({ allowH2: true }));
} catch (e) {}
SCRIPT

# Add to ~/.zshrc (or ~/.bashrc)
echo 'export NODE_OPTIONS="--require $HOME/.node/enable-h2.cjs"' >> ~/.zshrc

This makes every Node.js process (Next.js, Inngest, scripts, tests) negotiate HTTP/2 automatically. If the server only supports HTTP/1.1, it falls back gracefully. Zero code changes required in any project.

Important limitation: this does NOT affect Claude Code itself (it's a Bun binary, not Node.js) or the AWS SDK (@aws-sdk/client-s3), which uses its own HTTP client.

Layer 2: Fix macOS TCP sysctl defaults

# Temporary (until reboot)
sudo sysctl -w net.inet.tcp.mssdflt=1440 net.inet.tcp.blackhole=0 \
  net.inet.tcp.msl=15000 net.inet.tcp.delayed_ack=0 net.inet.tcp.fastopen=3

For persistence, create /Library/LaunchDaemons/custom.sysctl.tuning.plist with those values.

| Parameter | macOS default | Fixed | Why |
|---|---|---|---|
| tcp.mssdflt | 512 | 1440 | 512 is dial-up era; 1440 is correct for MTU 1492 |
| tcp.blackhole | 1 | 0 | Was silently dropping packets instead of RST |
| tcp.msl | 2000 | 15000 | TIME_WAIT was 4s, way too aggressive |
| tcp.delayed_ack | 3 | 0 | Immediate ACK, less latency |

Layer 3: For apps using AWS SDK / S3Client (Cloudflare R2, S3, etc.)

The AWS SDK v3 uses node:https internally with no keepAlive by default — every PutObject, GetObject, etc. opens a new TCP connection. If your endpoint is Cloudflare R2 (*.r2.cloudflarestorage.com), this can trigger the same CDN-wide IP block:

import { NodeHttpHandler } from '@smithy/node-http-handler';
import { Agent as HttpsAgent } from 'node:https';

const s3Client = new S3Client({
  // ... your config ...
  maxAttempts: 5,
  requestHandler: new NodeHttpHandler({
    httpsAgent: new HttpsAgent({ keepAlive: true, maxSockets: 25 }),
    connectionTimeout: 10_000,
    socketTimeout: 120_000,
  }),
});

What does NOT work

| Approach | Result |
|---|---|
| dns.setDefaultResultOrder("ipv4first") | Makes it worse (1/100 success) — disables Happy Eyeballs RFC 8305 |
| Increasing http.globalAgent.maxSockets | Doesn't help — more sockets = more refusals from server |
| Semaphore/queue to limit concurrency | Palliative — complex and fragile, HTTP/2 solves it at the root |
| sysctl tuning alone | Doesn't solve it — helps marginally but root cause is HTTP/1.1 |

How this relates to other comments in this thread

  • @brigleb found tcp.mssdflt=512 from Tailscale — that's Layer 2 above. But even after fixing MSS, HTTP/1.1 connection floods can still trigger CDN blocks.
  • @dacort fixed their issue by setting MTU to 1492 — this is related but different. MTU affects packet fragmentation at the network level, while MSS affects TCP segment size. Both should be aligned.
  • @yasserlens and my previous comment about VPN residue / utun interfaces — VPNs often set mssdflt=512 AND create routing conflicts. Removing the VPN fixes both, but you can also fix MSS directly without removing the VPN.
  • @CaptainMcCrank saw connections ESTABLISHED then immediately CLOSED — this is consistent with CDN rate limiting. The TCP handshake succeeds but the CDN resets the connection when it detects too many concurrent connections from the same IP. Reboot fixed it because it cleared all accumulated TCP state.

Diagnostic one-liner

# Check if you're affected
sysctl net.inet.tcp.mssdflt net.inet.tcp.blackhole
# If mssdflt=512 or blackhole=1, you have the problem

For the Claude Code team

Claude Code itself (Bun binary) likely already uses HTTP/2 via Bun's fetch. But the subprocesses it spawns (MCP servers, tool calls, etc.) that use Node.js would be affected by HTTP/1.1 connection exhaustion. If Claude Code is making many concurrent API calls (parallel tool use, streaming + telemetry + marketplace refresh simultaneously), the HTTP/1.1 connections from those Node.js subprocesses could be triggering CDN rate limiting that then also affects the main Bun process.

Worth considering: should Claude Code set NODE_OPTIONS with allowH2 for spawned Node.js subprocesses by default?

mateuszkotarba · 5 months ago

Mac M1. For me claude worked on mobile hotspot but not on my home wifi. I struggle with that few weeks. I had to remove all network configurations by these commands:

sudo rm /Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist
sudo rm /Library/Preferences/SystemConfiguration/com.apple.network.identification.plist
sudo rm /Library/Preferences/SystemConfiguration/com.apple.wifi.message-tracer.plist
sudo rm /Library/Preferences/SystemConfiguration/NetworkInterfaces.plist
sudo rm /Library/Preferences/SystemConfiguration/preferences.plist

then reboot

Now it works. Maybe this will help someone.

martc03 · 4 months ago

Still experiencing this on macOS (Darwin 25.3.0) with Claude Code using Opus 4.6.

  • ECONNRESET errors on both Wi-Fi and ethernet connections
  • Short HTTP requests (curl) work fine — 10/10 succeed instantly (~0.2s)
  • Only Claude Code's long-lived streaming connections are affected
  • Retries eventually succeed but responses take 4x longer cycling through all retry attempts
  • Network is stable — problem is isolated to Claude Code
  • Confirmed not a firewall, DNS, proxy, or routing issue

Consistent with the HTTP/2 stale connection pooling theory from #23744.

MarceloCajueiro · 4 months ago

After 8 days after my post (https://github.com/anthropics/claude-code/issues/5674#issuecomment-3881326808) I can say properly that worked for me forcing to use HTTP/2.

woytekbode · 4 months ago

I have the same problem using Claude Cowork on windows.

abdullahghani99 · 4 months ago
OK after tons of troubleshooting, it turns out the presence of Tailscale on my Mac, which I forgot I had installed and which was periodically launching, was my problem. This was causing intermittent failures, only on certain network stacks, that was driving me insane. Wanted to post this here to help others who might have this issue. Check your network and VPN setup!! Update: More specifically, Tailscale (I suspect) set my TCP MSS (max segment size) to 512. Insane. Should be 1460, and presumably many apps expect that value. As Claude reports: > A TCP MSS of 512 is absurdly low—it's fragmenting your packets into tiny pieces, which would absolutely cause issues with HTTP/2 streaming while basic HTTP requests (like curl) might still work. You can temporarily fix this on your Mac with sudo sysctl -w net.inet.tcp.mssdflt=1460 but that does not persist after reboot. I found it very difficult to remove residual Tailscale daemons, they stayed even after it was _completely_ removed from System Settings. But maybe by adjusting that setting you can use Claude Code to track it down, which is what I did!

This really helped me, man. Thanks a lot. IOU

pacificsky · 4 months ago

This might be useful for debugging for some scenarios
https://gist.github.com/pacificsky/5a4e85aa98a9c88110dd534c819390f9

wozman · 3 months ago

I had exactly the same problem on a brand new freshly installed MacBook Pro, so no VPN cruft, etc etc.
claude -p "<prompt>" worked fine, but interactive mode gave me consistent ECONNRESET errors.
Dropping the MTU on my Mac from 1500 down to 1450 fixed it for me. Setting it back to anything higher instantly gave me ECONNRESETs again, switch it back down to 1450 fixed. This was 100% reproducible.
You may have to tweak the numbers depending on your network setup, perhaps down as far as 1436.

Tilotiti · 3 months ago

Fun fact ...

My project is using Symfony Proxy in order to run my app.

Claude decided to create a startup script, and created the json file .claude/settings.json :

{
  "env": {
    "http_proxy": "http://localhost:7080",
    "https_proxy": "http://localhost:7080"
  }
}

It launched the preview but the Symfony proxy was not running.

So all the Claude session requests, not only the project one, where going through this proxy that was not running because claude named the proxy environment variable the same as it seems to be using internally.

Starting the Symfony Proxy made it work.

I deleted the settings files and just run it manually. Working fine now.

l3ntes · 3 months ago

Same problem here, with more tan 60 times on a morning

sibelius · 2 months ago

this is also happening for me inside a linux container running inside proxmox

API Error: The socket connection was closed unexpectedly. For more information, pass verbose: true in the second argument to fetch()

wyatt-halluminate · 2 months ago

Me as well, never used to get this, am getting it several times a day now.

sibelius · 2 months ago

This was related to a bug if IPv6 not resolving in my machine

ddeellttaa · 2 months ago
## Update: Found a deeper root cause beyond VPN/sysctl — Node.js HTTP/1.1 connection exhaustion I commented here before about VPN residue and utun interfaces. That was real but turned out to be only part of the problem. After months of dealing with this intermittently, I finally spent a full day doing systematic testing and found the actual root cause — and it's something that affects every Node.js app on macOS, not just Claude Code. ### The real problem Node.js fetch (via undici) uses HTTP/1.1 by default, which opens one TCP connection per request. CDNs like Cloudflare limit concurrent TCP connections per IP to roughly 50-150. When any Node.js process on your machine exceeds that threshold, the server starts refusing connections with ECONNRESET/ECONNREFUSED. Here's the critical part that explains why your browser stops working too: Cloudflare uses shared IPs. When your Node.js app floods 160.79.104.10:443 (Anthropic's Cloudflare-fronted API) with hundreds of HTTP/1.1 connections, Cloudflare can temporarily block your IP for all domains behind that CDN — GitHub, NPM, Discord, and thousands of other sites suddenly become unreachable from your browser. curl and browsers don't have this problem because they use HTTP/2 by default, which multiplexes all requests over a single TCP connection. ### Proof I ran a controlled test battery against external APIs: Method 200 concurrent requests 300 concurrent fetch (HTTP/1.1) 4 ok, 196 fail worse https.get (HTTP/1.1) 3 ok, 197 fail worse http2 (native) 200 ok, 0 fail 300 ok, 0 fail fetch + allowH2 200 ok, 0 fail 300 ok, 0 fail localhost (any method) 1000 ok, 0 fail — Localhost never fails because there's no per-IP connection limit. The problem is exclusively with remote servers. ### Why this is particularly bad on macOS macOS has some TCP defaults that make this worse: tcp.mssdflt = 512 (dial-up era value — several people in this thread found this, including @brigleb with Tailscale) tcp.blackhole = 1 (silently drops packets instead of sending RST) tcp.msl = 2000 (TIME_WAIT only 4 seconds — connections get recycled too aggressively) These sysctl values compound the HTTP/1.1 connection flood problem. VPNs like Tailscale, TunnelBear, and Cloudflare WARP can also set mssdflt to 512, which fragments packets and makes HTTP/2 streaming fail even when the protocol negotiation works. ### How I'm solving it (3 layers) Layer 1: Enable HTTP/2 globally for all Node.js processes # Install undici in a shared location mkdir -p ~/.node && cd ~/.node npm init -y && npm install undici # Create preload script: ~/.node/enable-h2.cjs cat > ~/.node/enable-h2.cjs << 'SCRIPT' try { const { setGlobalDispatcher, Agent } = require( require('path').join(require('os').homedir(), '.node/node_modules/undici') ); setGlobalDispatcher(new Agent({ allowH2: true })); } catch (e) {} SCRIPT # Add to ~/.zshrc (or ~/.bashrc) echo 'export NODE_OPTIONS="--require $HOME/.node/enable-h2.cjs"' >> ~/.zshrc This makes every Node.js process (Next.js, Inngest, scripts, tests) negotiate HTTP/2 automatically. If the server only supports HTTP/1.1, it falls back gracefully. Zero code changes required in any project. Important limitation: this does NOT affect Claude Code itself (it's a Bun binary, not Node.js) or the AWS SDK (@aws-sdk/client-s3), which uses its own HTTP client. Layer 2: Fix macOS TCP sysctl defaults # Temporary (until reboot) sudo sysctl -w net.inet.tcp.mssdflt=1440 net.inet.tcp.blackhole=0 \ net.inet.tcp.msl=15000 net.inet.tcp.delayed_ack=0 net.inet.tcp.fastopen=3 For persistence, create /Library/LaunchDaemons/custom.sysctl.tuning.plist with those values. Parameter macOS default Fixed Why tcp.mssdflt 512 1440 512 is dial-up era; 1440 is correct for MTU 1492 tcp.blackhole 1 0 Was silently dropping packets instead of RST tcp.msl 2000 15000 TIME_WAIT was 4s, way too aggressive tcp.delayed_ack 3 0 Immediate ACK, less latency Layer 3: For apps using AWS SDK / S3Client (Cloudflare R2, S3, etc.) The AWS SDK v3 uses node:https internally with no keepAlive by default — every PutObject, GetObject, etc. opens a new TCP connection. If your endpoint is Cloudflare R2 (*.r2.cloudflarestorage.com), this can trigger the same CDN-wide IP block: import { NodeHttpHandler } from '@smithy/node-http-handler'; import { Agent as HttpsAgent } from 'node:https'; const s3Client = new S3Client({ // ... your config ... maxAttempts: 5, requestHandler: new NodeHttpHandler({ httpsAgent: new HttpsAgent({ keepAlive: true, maxSockets: 25 }), connectionTimeout: 10_000, socketTimeout: 120_000, }), }); ### What does NOT work Approach Result dns.setDefaultResultOrder("ipv4first") Makes it worse (1/100 success) — disables Happy Eyeballs RFC 8305 Increasing http.globalAgent.maxSockets Doesn't help — more sockets = more refusals from server Semaphore/queue to limit concurrency Palliative — complex and fragile, HTTP/2 solves it at the root sysctl tuning alone Doesn't solve it — helps marginally but root cause is HTTP/1.1 ### How this relates to other comments in this thread @brigleb found tcp.mssdflt=512 from Tailscale — that's Layer 2 above. But even after fixing MSS, HTTP/1.1 connection floods can still trigger CDN blocks. @dacort fixed their issue by setting MTU to 1492 — this is related but different. MTU affects packet fragmentation at the network level, while MSS affects TCP segment size. Both should be aligned. @yasserlens and my previous comment about VPN residue / utun interfaces — VPNs often set mssdflt=512 AND create routing conflicts. Removing the VPN fixes both, but you can also fix MSS directly without removing the VPN. * @CaptainMcCrank saw connections ESTABLISHED then immediately CLOSED — this is consistent with CDN rate limiting. The TCP handshake succeeds but the CDN resets the connection when it detects too many concurrent connections from the same IP. Reboot fixed it because it cleared all accumulated TCP state. ### Diagnostic one-liner # Check if you're affected sysctl net.inet.tcp.mssdflt net.inet.tcp.blackhole # If mssdflt=512 or blackhole=1, you have the problem ### For the Claude Code team Claude Code itself (Bun binary) likely already uses HTTP/2 via Bun's fetch. But the subprocesses it spawns (MCP servers, tool calls, etc.) that use Node.js would be affected by HTTP/1.1 connection exhaustion. If Claude Code is making many concurrent API calls (parallel tool use, streaming + telemetry + marketplace refresh simultaneously), the HTTP/1.1 connections from those Node.js subprocesses could be triggering CDN rate limiting that then also affects the main Bun process. Worth considering: should Claude Code set NODE_OPTIONS with allowH2 for spawned Node.js subprocesses by default?

dude, you are absolute genius!

i11010520 · 2 months ago
### Diagnostic one-liner # Check if you're affected sysctl net.inet.tcp.mssdflt net.inet.tcp.blackhole # If mssdflt=512 or blackhole=1, you have the problem ### For the Claude Code team Claude Code itself (Bun binary) likely already uses HTTP/2 via Bun's fetch. But the subprocesses it spawns (MCP servers, tool calls, etc.) that use Node.js would be affected by HTTP/1.1 connection exhaustion. If Claude Code is making many concurrent API calls (parallel tool use, streaming + telemetry + marketplace refresh simultaneously), the HTTP/1.1 connections from those Node.js subprocesses could be triggering CDN rate limiting that then also affects the main Bun process. Worth considering: should Claude Code set NODE_OPTIONS with allowH2 for spawned Node.js subprocesses by default?

@MarceloCajueiro Thanks for your deep insight, but it seems not work for me.

Error

Whatever claude or ollama launch claude, it will prompt 'Unable to connect to API (ECONNREFUSED)' even for '/init'.

Verify: node.js fetch

[concurrent-fetch.js]

// Node built-in fetch)

const URL = 'https://example.com'; // change this
const CONCURRENCY = 500;

async function fetchOnce(i) {
  try {
    const res = await fetch(URL);
    const text = await res.text();
    console.log(`#${i} → ${res.status} (${text.length} bytes)`);
    return res.status;
  } catch (err) {
    console.error(`#${i} → ERROR`, err.message);
    return null;
  }
}

async function main() {
  console.time('200 requests');

  const tasks = Array.from({ length: CONCURRENCY }, (_, i) =>
    fetchOnce(i + 1)
  );

  const results = await Promise.all(tasks);

  console.timeEnd('200 requests');

  const success = results.filter(s => s === 200).length;
  console.log(`Success: ${success}/${CONCURRENCY}`);
}

main();

Then the testing result says that fetch works fine, not like what you said "4 ok, 196 fail".

Success: 200/200
Success: 500/500

Try Layer 1

mkdir -p ~/.node && cd ~/.node

cat > ~/.node/enable-h2.cjs << 'SCRIPT'
try {
  const { setGlobalDispatcher, Agent } = require(
    require('path').join(require('os').homedir(), '.node/node_modules/undici')
  );
  setGlobalDispatcher(new Agent({ allowH2: true }));
} catch (e) {}
SCRIPT

export NODE_OPTIONS="--require $HOME/.node/enable-h2.cjs"

Then claude or ollama launch claude still same error.

Try Layer 2

❯ sudo sysctl -w net.inet.tcp.mssdflt=1440 net.inet.tcp.blackhole=0 \
  net.inet.tcp.msl=15000 net.inet.tcp.delayed_ack=0 net.inet.tcp.fastopen=3
Password:
net.inet.tcp.mssdflt: 512 -> 1440
net.inet.tcp.blackhole: 0 -> 0
net.inet.tcp.msl: 15000 -> 15000
net.inet.tcp.delayed_ack: 3 -> 0
net.inet.tcp.fastopen: 3 -> 3

Then claude or ollama launch claude still same error.

What a pity.

SnowLukin · 2 months ago
@MarceloCajueiro Thanks for your deep insight, but it seems not work for me.

Same. Did all the recommended steps – Claude does few responses and then gets stuck. Gets unstuck after ide reboot but then gets stuck again. Pretty annoying problem.
jfyi: I have vpn on (can't test without it for my own reasons)

viniciusmorgado · 2 months ago

Any solution for this? I'm getting the same issue on an M4, and I never uninstalled any VPN service. The same doesn’t seem to happen on Linux or Windows on the same network. I need to send the same prompt multiple times just to get it to work.

YeKc1M · 2 months ago

Same issue happens in my mac m4 and wsl. I never uninstall any VPN service.
When I start some a little long and complex work, it will be stuck and get the error.

rjk-laconic · 2 months ago

i just started getting this issue today out of nowhere.

romancone · 2 months ago

This issue is highly visible in version 2.1.143

Claude 2.1.126 still works fine

SnowLukin · 2 months ago

I started using claude desktop app and never seen that problem ever since. Only happens in cursor for me

pangpond · 2 months ago

It's happen to me to after electric drop. I try many way but didn't resolve.
last shot I try to connect with Warp by Cloudflare...It's fix!!!

brunos3d · 1 month ago

Root cause identified — Bun does not set SO_KEEPALIVE on TCP sockets

Posting here as this is the most active thread. We did a deep binary-level investigation on Linux (Arch/Manjaro) and found the root cause. Full report in #60133.

What's happening

The claude binary is a standalone Bun 1.3.14 executable (confirmed via strings). Bun's fetch() does not set SO_KEEPALIVE on its TCP sockets. During agentic sessions, the HTTP/2 connection sits idle while tools execute locally. Without keepalive probes, the connection is silently dropped by the router, ISP, or Anthropic's server — and Bun only discovers this when it tries to reuse the connection for the next API call.

Confirmed via ss -tnop state established '( dport = :443 )':

# Every other app on the system:
chrome  → 34.194.3.76:443   timer:(keepalive,16sec,0)   ← SO_KEEPALIVE ON
code    → 4.228.31.153:443  timer:(keepalive,39sec,0)   ← SO_KEEPALIVE ON

# Claude Code:
claude  → 160.79.104.10:443 (no timer)                  ← SO_KEEPALIVE NOT SET

Additionally, all application-level keepalives are explicitly disabled via GrowthBook feature flags in ~/.claude.json:

"heartbeat_interval_ms": 0,
"session_keepalive_interval_ms": 0,
"session_keepalive_interval_v2_ms": 0

Why Cloudflare Warp fixes it on macOS

Warp routes traffic through a VPN tunnel that enforces its own keepalive at the tunnel layer — effectively compensating for the missing SO_KEEPALIVE in Bun. That's the same reason it "just works" through Warp.

Workaround (Linux/macOS)

Add to ~/.zshrc or ~/.bashrc:

export CLAUDE_CODE_REMOTE_SEND_KEEPALIVES=true
export BUN_CONFIG_HTTP_IDLE_TIMEOUT=300
export BUN_CONFIG_HTTP_RETRY_COUNT=3
export NODE_OPTIONS="--dns-result-order=ipv4first"

For a more reliable OS-level fix, build an LD_PRELOAD shim that forces SO_KEEPALIVE on every TCP socket:

cat > /tmp/ka.c << 'CSRC'
#define _GNU_SOURCE
#include <stddef.h>
#include <sys/socket.h>
#include <dlfcn.h>
int socket(int domain, int type, int protocol) {
    static int (*orig)(int,int,int);
    if (!orig) orig = dlsym(RTLD_NEXT, "socket");
    int fd = orig(domain, type, protocol);
    if (fd >= 0 && (type & 0xf) == SOCK_STREAM) {
        int v = 1; setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &v, sizeof(v));
    }
    return fd;
}
CSRC
mkdir -p ~/.local/lib
gcc -shared -fPIC -O2 -o ~/.local/lib/libkeepalive.so /tmp/ka.c -ldl
echo 'export LD_PRELOAD="$HOME/.local/lib/libkeepalive.so${LD_PRELOAD:+:$LD_PRELOAD}"' >> ~/.zshrc

Also lower the TCP keepalive probe interval:

sudo sysctl -w net.ipv4.tcp_keepalive_time=60
echo "net.ipv4.tcp_keepalive_time=60" | sudo tee /etc/sysctl.d/99-claude-keepalive.conf

Verified: After applying these fixes, all claude sockets show timer:(keepalive,Xs,0) and two consecutive 90-second idle windows survive without error.

Suggested fix for Anthropic

Set SO_KEEPALIVE on the API socket — this is a one-liner and would fix the majority of reports across all platforms:

socket.setKeepAlive(true, 30000);

Full analysis, evidence table, and binary findings: #60133

searchingtofind · 1 month ago

Brunos3d this is the way!

CamiloEspinoza · 1 month ago

Same ECONNRESET, but in our case the root cause was a network-layer Path-MTU black hole — not keepalive. Sharing because the fix is completely different, and the keepalive workarounds above wouldn't have helped us. (Also, heads-up for macOS readers: the LD_PRELOAD shim and sysctl net.ipv4.* snippets are Linux-only — they're no-ops on macOS, where the signed/hardened claude binary also ignores DYLD_INSERT_LIBRARIES.)

The tell: it wasn't only Claude Code. Chrome also failed with ERR_CONNECTION_RESET on specific sites (Shopify-hosted stores), while Safari and curl loaded them fine on the same network. A Claude/Bun bug can't explain Chrome breaking too — so we packet-captured it.

What tcpdump showed (every connection attempt, fully deterministic):

192.168.1.93 > 23.227.38.74.443: Flags [S]                          # SYN      ok
23.227.38.74 > 192.168.1.93:     Flags [S.]                         # SYN-ACK  ok
192.168.1.93 > 23.227.38.74.443: Flags [.],  seq 1:1449  len 1448   # ClientHello part 1  -> 1500-byte packet
192.168.1.93 > 23.227.38.74.443: Flags [P.], seq 1449:1828 len 379  # ClientHello part 2  -> small packet
23.227.38.74 > 192.168.1.93:     Flags [R]                          # RESET
23.227.38.74 > 192.168.1.93:     Flags [.], ack 1, sack {1449:1828} # "I only ever got the SMALL segment"

That sack {1449:1828} together with ack 1 is the smoking gun: the far end received the small segment but never the full-size 1500-byte one. The path silently drops 1500-byte packets and no ICMP "fragmentation needed" comes back, so PMTUD never engages → the handshake stalls and the connection is reset.

Why this maps onto the reports in this thread:

  • curl / Safari work → their TLS ClientHello is small and fits in a sub-1500 packet.
  • Chrome and claude (Bun → BoringSSL) fail → their ClientHello is large (>1448 B) and requires a full 1500-byte segment, which is exactly what gets dropped.
  • Disabling Kyber / ECH / QUIC in Chrome changed nothing → the hello is still >1448 B and still needs that big segment.
  • A VPN / tunnel / Cloudflare Warp "fixes" it → not (only) because of keepalive, but because the tunnel re-encapsulates traffic with a smaller inner MTU/MSS, so the inner segments fit under the path limit. Same mechanism as @brigleb's Tailscale setting MSS to 512, and it matches @dacort's MTU 1492 fix exactly.

Fix (macOS, persistent):

# find your service name first:  networksetup -listallnetworkservices
sudo networksetup -setMTU "Wi-Fi" 1400     # or 1492 if your path supports it (PPPoE/fiber)

After this, Claude Code stopped throwing ECONNRESET and the sites loaded in Chrome — with no tunnel at all.

30-second triage to tell the two failure modes apart:

  • curl https://api.anthropic.com/ + Safari are fine, but Chrome / Claude / Cursor reset on big-ClientHello sites → it's MTU/MSS → lower your MTU.
  • Everything works initially and only drops after an idle period while tools run → that's the SO_KEEPALIVE class @brunos3d described.

So there seem to be (at least) two distinct buckets getting merged here: (1) idle-connection drops (the keepalive story) and (2) large-ClientHello packets dying in a PMTU black hole / clamped MSS (ours, @dacort, @brigleb). socket.setKeepAlive(true, …) would address bucket 1 but not bucket 2.

Suggestion for the team: on ECONNRESET, printing a hint like "if Chrome also fails on some sites but Safari/curl don't, try lowering your MTU (networksetup -setMTU …)" would have saved us a long debugging session.

searchingtofind · 1 month ago

Honestly... I'm not sure who else's has tried this.. I reset my computer. That was it. The econnrest disappeared. The instances were old.. they had been massive sessions with a lot of context and left open for weeks. I always save my sessions this way, before creating handoff prompts. Resetting my entire computer, often I leave it in sleep, or if I have an automated task, it runs silently all night. Nothing anyone suggested here worked.. until I just ended all instances and reset my computer

nikspyratos · 1 month ago

I've been having this in "longer-lived" sessions where "longer-lived" is something like 1h+, regardless of context size. Stopping and resuming the session doesn't work, occasionally I can get it to succeed in writing a handoff doc for a new session to continue. So it's not just "week long massive sessions", as I restart my CC frequently. Not having similar issues in other browsers

/feedback doesn't work either in the sessions, so you can't even report the bug within the session! Same ECONNRESET.

Hopefully #60133 is the real issue, but damn this has been very frequent as of this week for me, it's hard to get the large work CC is useful for done.

Seems tons of similar issues since last year already:
https://github.com/anthropics/claude-code/issues/66758
https://github.com/anthropics/claude-code/issues/56017
https://github.com/anthropics/claude-code/issues/28557
https://github.com/anthropics/claude-code/issues/66090
https://github.com/anthropics/claude-code/issues/64453
https://github.com/anthropics/claude-code/issues/60441
https://github.com/anthropics/claude-code/issues/65028
https://github.com/anthropics/claude-code/issues/62045
https://github.com/anthropics/claude-code/issues/62263
https://github.com/anthropics/claude-code/issues/56059
https://github.com/anthropics/claude-code/issues/62327
https://github.com/anthropics/claude-code/issues/66127
https://github.com/anthropics/claude-code/issues/41242
https://github.com/anthropics/claude-code/issues/62971
https://github.com/anthropics/claude-code/issues/57222
https://github.com/anthropics/claude-code/issues/51164
https://github.com/anthropics/claude-code/issues/63559

mmisztal1980 · 1 month ago

I've just run into this issue today, and made progress with Fable 5, here's what we've found:

ECONNRESET on large payloads — root cause: IPv6 PMTUD black hole (postmortem)

Environment: macOS (Apple Silicon), Claude Code CLI + VS Code extension anthropic.claude-code 2.1.170, home network (Polish ISP, IPv6 prefix 2a02:...), Ethernet via Dell Universal Dock D6000 (en7), no VPN.

TL;DR: Persistent ECONNRESET errors affecting large requests only (/compact, large document reviews via subagents) were caused by a Path MTU Discovery black hole on the ISP's IPv6 path, not by Claude Code or the local TCP stack. Small requests succeeded; any request with a large upload silently stalled mid-transfer and eventually reset. Fix: disable IPv6 on the active network interface. A separate, unrelated issue — Opus retry loops that also surfaced as ECONNRESET — was server-side load shedding during the June 8–10 incident window (confirmed via TCP state: server-initiated FIN after a clean 746 KB upload) and required no local action. Section 6 has a one-command discriminator table for telling the two apart.

---

Symptoms

  1. ECONNRESET errors in VS Code extension tabs, "all the time" — but intermittent in terminal sessions.
  2. /compact on a long-running (4.5h) session failed repeatedly:

``
⎿ Error: Error during compaction: API Error: Unable to connect to API (ECONNRESET)
``
One attempt took ~15 minutes to fail, stuck at 95% for the last ~10 minutes. Reproducible across models (failed identically on Sonnet) and across fresh processes.

  1. A subagent task reviewing two documents (~1,400 lines total) hung silently for 7+ minutes with the token counter frozen — no error, no progress.
  2. Long-running Opus job: 4h24m elapsed, only 27.3k tokens — i.e. spent the time in retry/backoff loops, not inference.
  3. Onset correlated with the Claude Fable 5 release window (~June 9–10, 2026).

Key distinguishing pattern

Small chatty requests work; large uploads hang silently, then reset.

This is the signature that separates a PMTUD black hole from generic connectivity problems, server outages, or connection-pool exhaustion. If everything fails, it's not this. If only big payloads fail, check the items below.

---

What to check, how, and what to expect

1. Which protocol and interface your traffic is actually using

pgrep -fl claude                 # find the claude PID (beware: matches Claude.app helpers too)
nettop -p <PID> -L 2             # two samples of per-connection byte counters

What we found (the smoking gun):

tcp6 2a02:a310:...:b710.54592<->2607:6bc0::10.443,en7,Established,
     bytes_in=12828, bytes_out=857613, rx_dupe=27, ...
# counters IDENTICAL across both samples → zero forward progress

What to read from it:

| Observation | Meaning |
|---|---|
| tcp6 to 2607:6bc0::10 | Traffic is on IPv6 — IPv4-targeted fixes (sysctl net.inet.tcp.mssdflt, netstat greps for 160.79.x) don't apply |
| Interface en7 (dock), not en0 (Wi-Fi) | MTU fixes applied to Wi-Fi were applied to the wrong interface |
| ~857 KB out vs ~13 KB in, counters frozen, dupes/retransmits | Large upload stalled mid-flight — classic PMTUD black hole: oversized segments dropped, ICMPv6 Packet Too Big never arrives, TCP retransmits into the void forever |

Pitfall we hit: the connection census netstat -an | grep "160.79.10" came back nearly empty and looked "clean" — because it only matched IPv4. Always check both stacks:

netstat -an | grep -E "160\.79\.|2607:6bc0" 

2. The mechanism (why large payloads specifically)

IPv6 routers never fragment. If a TCP segment exceeds any hop's MTU, that hop must drop it and send ICMPv6 Packet Too Big back so the sender shrinks its segments. If a middlebox filters those ICMPv6 messages (common on DS-Lite / tunneled IPv6 deployments), the sender never learns — full-size segments vanish silently, retransmits vanish too, the connection freezes, and after several minutes one side gives up → ECONNRESET.

  • Small requests (chat turns, tool results) fit under the real path MTU → fine.
  • /compact (ships the entire transcript in one POST) and large file reads → guaranteed full-size segments → guaranteed stall.
  • An interface MTU of 1450 did not help, because the true path MTU was lower still and PMTUD couldn't communicate it.

3. The fix

Identify the network service for the active interface and disable IPv6 on it:

networksetup -listallhardwareports          # map device (en7) → service name
sudo networksetup -setv6off "Dell Universal Dock D6000"
networksetup -getinfo "Dell Universal Dock D6000"   # verify: "IPv6: Off"

This setting is stored in the network service configuration and persists across reboots — no LaunchDaemon needed. Revert later with -setv6automatic.

Result: the identical subagent task that had hung for 7+ minutes with a frozen counter completed normally immediately after the switch (visible token streaming, multi-tool-use review finished). tcp4 connections to 160.79.x took over.

4. Optional: keep IPv6 by finding the true path MTU

# binary-search with don't-fragment pings against the API's v6 address
ping6 -D -s 1400 2607:6bc0::10     # fails → path MTU below ~1448
ping6 -D -s 1232 2607:6bc0::10     # should succeed (1280 minimum + headers)

Walk the size to the cliff, set the interface MTU to cliff + 48 via networksetup -setMTU. If moderate sizes black-hole with -D, the ISP path is eating ICMPv6 PTB (an RFC 4890 violation worth reporting to them); leave v6 off until fixed.

5. What did NOT fix it (tried first, ruled out)

| Attempted fix | Outcome | Why it couldn't work |
|---|---|---|
| NODE_OPTIONS undici allowH2 preload (from #5674) | No effect | Claude Code is a Bun binary — the Node preload never loads in the main process; also GUI-launched VS Code doesn't inherit shell env |
| sysctl net.inet.tcp.mssdflt=1440 etc. | No effect on this bug | Tunes the IPv4 stack; traffic was on IPv6 (net.inet6.tcp6.mssdflt is the v6 knob). Also sysctl -w is runtime-only — persists only via a LaunchDaemon plist |
| MTU 1492 on Wi-Fi | Seemed to help, didn't | Traffic was on en7 (dock), not en0 |
| pkill + fresh session / --resume | Recovered the session, not the bug | Fresh process, same broken v6 path |
| Switching models (Opus → Sonnet) | No effect on the resets | Network-layer problem, model-agnostic |

6. The unrelated second issue: Opus-side ECONNRESET (server load shedding)

Important caveat for anyone reading this issue: not every ECONNRESET is a network problem. After the network fix, the same review task succeeded under Sonnet but failed under Opus 4.7 (1M) — first cycling Retrying in 0s · attempt 5/10 (fast overload-class rejections), later escalating to Unable to connect to API (ECONNRESET) · Retrying in 11s · attempt 10/10. Same machine, same path, same payload; only the model differed. A network fault can never be model-dependent.

A nettop sample taken during the Opus failure proved the path was healthy and the close came from the server:

tcp4 192.168.0.122:62803<->160.79.104.10:443, en7, Established,  5201 in /  13299 out, 0 dupe, 0 re-tx, rtt 1ms
tcp4 192.168.0.122:64414<->160.79.104.10:443, en7, LastAck,      5228 in / 746526 out, 0 dupe, 0 re-tx, rtt 1ms
  • All connections tcp4 to 160.79.104.10 — the v6 fix held.
  • Zero retransmits/dupes, 1 ms RTT — clean transport.
  • The big request: 746 KB uploaded successfully, connection in LastAck — meaning the remote side sent FIN first. The server received the full payload and closed the connection instead of answering. That server-initiated close surfaces to the client as ECONNRESET.

This matched the elevated-error incidents on Opus 4.6/4.7/4.8 logged on status.claude.com around June 8–9 (Fable 5 launch window). No local action exists; use Sonnet or retry later.

One-command discriminator (nettop -p <claude_pid> -L 2, compare the two samples):

| Signal | PMTUD black hole (local/ISP path) | Server-side load shed |
|---|---|---|
| Byte counters across samples | frozen (no forward progress) | n/a — connection already closed |
| rx_dupe / re-tx | high (we saw 27 dupes) | zero |
| TCP state | Established but wedged | LastAck / CloseWait (remote FIN) |
| Model-dependent | no — hits all models equally | yes — one model fails, another works |
| Error appearance | silent multi-minute freeze, late reset | explicit retry attempts with backoff |

7. Why it "suddenly started"

The PMTUD black hole was a latent condition on the ISP's v6 path. The likely trigger (~June 9–10): client traffic to api.anthropic.com shifted to IPv6 — plausibly via Happy Eyeballs preferring a changed endpoint around the Fable release — moving bulk uploads onto the broken path for the first time. Cannot be proven after the fact; ISP-side routing changes are an equally valid trigger.

---

Suggestion for the Claude Code team

The client retried up to 11 attempts through a black-holed connection with a frozen spinner and surfaced only a generic ECONNRESET — this cost roughly a working day of local-stack debugging. Two cheap improvements:

  1. Detect mid-transfer stalls (bytes acknowledged stopped advancing during a large upload) and surface "upload stalled — possible path MTU issue" instead of a generic connection error.
  2. Log the protocol/address family and local interface for failing connections (tcp6 via en7 vs tcp4 via en0) — that single line in the error output would have shortcut this entire investigation.
  3. Distinguish reset origins in the error message. A server-initiated FIN/RST after a completed upload (load shedding) and a transport-level stall both surface as the same ECONNRESET string. The client has enough socket-level information to say "server closed the connection after receiving the request" vs. "connection stalled mid-upload" — these point to opposite ends of the wire and completely different remediations.
mmisztal1980 · 1 month ago

ECONNRESET follow-up — June 11: two distinct findings (IPv6 leak via second interface + server-side drops of large requests on Sonnet 4.6)

Environment: macOS (Apple Silicon), Claude Code CLI, home network (Polish ISP), Ethernet via Dell sUniversal Dock D6000 (en7), Wi-Fi (en0) up simultaneously. Follow-up to the June 9–10 postmortem (IPv6 PMTUD black hole, fixed by disabling IPv6 on the dock interface).

TL;DR: Two separate findings today.

  1. Local (fixed): Disabling IPv6 on the active interface was insufficient on a multi-homed machine — Wi-Fi was up alongside the dock with IPv6 still enabled, keeping a global v6 default route alive. Dual-stack connections could intermittently leak onto the broken ISP v6 path. Fix: disable IPv6 on every up interface; verify with ifconfig | grep -c "inet6.*2a02" → 0.
  2. Server-side (not fixable locally): With transport confirmed clean (all tcp4, 0 retransmits, 1 ms RTT), a Sonnet 4.6 session with ~680 KB request payloads had the same request dropped ~16 consecutive times — full upload accepted, server closes, no HTTP response — over ~25 minutes, with one partial success (719 tokens) that then stalled terminally. ~11 MB cumulative upload to deliver 719 tokens. No corresponding incident on status.claude.com. Mitigation: abandon the session; start fresh with a small seeded context; keep per-turn payloads small.

---

Finding 1: the IPv6 leak (multi-homed machine trap)

Symptom

A single ECONNRESET on Sonnet 4.6 (~14:17 CET) on a machine where the v6 black-hole fix had been applied and verified the previous day. Intermittent rather than reproducible — characteristically different from the constant failures of June 9–10.

Diagnosis

networksetup -getinfo "Dell Universal Dock D6000" | grep IPv6   # → IPv6: Off  (yesterday's fix held)
ifconfig | grep -c "inet6.*2a02"                                # → 2  ← global v6 addresses still active!
netstat -rn | grep "^default"

The routing table showed the leak:

default   192.168.0.1                       UGScg    en7      ← v4 via dock (fine)
default   192.168.0.1                       UGScIg   en0      ← v4 via Wi-Fi (fine)
default   fe80::ae22:5ff:fe1b:d4bc%en0      UGcIg    en0      ← **v6 default via Wi-Fi**

Wi-Fi was up alongside the dock with IPv6 enabled, advertising a v6 default route. Any dual-stack connection attempt (Happy Eyeballs) could pick v6 and land on the same broken PMTUD path. The intermittency follows: only connections that happened to choose v6 failed — frequency proportional to traffic, not deterministic.

Fix and verification

sudo networksetup -setv6off "Wi-Fi"          # persists in service config
ifconfig | grep -c "inet6.*2a02"             # → 0  (no global v6 addresses)
netstat -rn -f inet6 | grep "^default"       # → only fe80::%utunN link-local noise, no physical interface

Lesson

On a multi-homed machine, disabling IPv6 per-interface must cover every up interface, not just the active one. Verify at the address/route level (ifconfig, netstat -rn -f inet6), not at the service-config level. The remaining fe80::%utun* default routes are link-local only and cannot carry internet traffic.

---

Finding 2: server-side drops of large requests (Sonnet 4.6, no status-page incident)

Symptom

After the leak was closed (transport verifiably clean), an active session kept failing/retrying. Token counter advanced to 719 and froze.

Evidence — nettop -p <pid> -L 2 during the failure window (~15:22 CET)

Process totals: ~10.1 MB out / ~314 KB in. Connection table (abridged; all tcp4 on en7, all 0 re-tx / 0 dupe / 1 ms RTT):

tcp4 ...:59006 <-> 160.79.104.10:443  Established   7401 in /  547476 out   ← live attempt
tcp4 ...:50062 <-> 160.79.104.10:443  Closing      11259 in /  681573 out   ┐
tcp4 ...:63813 <-> 160.79.104.10:443  Closing      10856 in /  674514 out   │
tcp4 ...:53744 <-> 160.79.104.10:443  Closing      10041 in /  688633 out   │
tcp4 ...:59452 <-> 160.79.104.10:443  Closing      12281 in /  675396 out   │ ~15 connections,
tcp4 ...:55668 <-> 160.79.104.10:443  Closing      10259 in /  672257 out   │ each ~600–710 KB
tcp4 ...:56539 <-> 160.79.104.10:443  Closing       9082 in /  712595 out   │ uploaded, ~10 KB
...                                                                          │ received, closed
tcp4 ...:53529 <-> 160.79.104.10:443  LastAck      10604 in /  466627 out   ┘ by remote (FIN)

Interpretation:

  • Transport is perfect — zero retransmits, zero dupes, 1 ms RTT, all IPv4. This is not the network.
  • ~15 connections each carrying a complete ~680 KB upload, then closed by the server (Closing/LastAck = remote-initiated teardown) without an HTTP response — a proper 4xx/5xx would surface as an API error, not ECONNRESET. The client retried the identical request ~16 times; cumulative upload ≈ 16 × 680 KB ≈ 11 MB.
  • One attempt partially succeeded (719 tokens streamed), then stalled.

Confirming the stall — spaced sampling

nettop -L 2 samples ~300 ms apart — too short to judge a slow stream. Use spaced samples:

nettop -p <pid> -L 3 -s 10     # three samples, 10 s apart

Result: port 59006 frozen at 547,476 out / 7,401 in across all three samples (20 s) — zero bytes moved process-wide. Upload complete, server silent, connection wedged. Verdict: abandoned.

Why the payload was ~680 KB per turn

The session had previously read two design documents (~1,400 lines) plus subagent results into context. Claude Code re-sends accumulated context on every turn, so the session's own history became a ~680 KB per-message toll. On a healthy edge this is invisible; on a degraded edge, context size converts directly into failure probability — each turn is another large-request lottery ticket.

Mitigation

  1. Stop the retry loop — after the same bytes are rejected N times, attempt N+1 has no new information. Esc/kill.
  2. Do not /compact a struggling large session — compaction is itself one of the largest requests the client makes; it has worse odds than the failing turns.
  3. Fresh session, seeded small — a one-paragraph task summary + file references. The first turn of a fresh session is tiny and gets through.
  4. Operationally: compact early. Compact at natural checkpoints while the transcript is small, not heroically at hour four. On days with edge instability, keep per-turn payloads lean (work from diffs, not full re-reads).

Status-page correlation: none

No incident was logged for Sonnet 4.6 in the failure window (June 11, ~14:00–15:30 CET). The most recent logged incident was Haiku 4.5 on June 10. This failure mode — ~6% success rate for large requests with clean transport — was apparently below Anthropic's alerting threshold, which is itself a useful data point for the report.

---

Updated discriminator table (supersedes the June 10 version)

One command — nettop -p <claude_pid> -L 3 -s 10 — separates three ECONNRESET origins:

| Signal | PMTUD black hole (ISP path) | Server load shed / overload | Server drops large requests (today) |
|---|---|---|---|
| Protocol/route | tcp6, or v6 default route present | tcp4, clean | tcp4, clean |
| re-tx / rx_dupe | high (retransmitting into void) | zero | zero |
| Byte counters | frozen mid-upload | n/a (closed fast) | full upload out, ~10 KB in, then frozen/closed |
| TCP states | Established, wedged | LastAck/CloseWait | many Closing/LastAck, one ~identical large upload each |
| Repetition pattern | every large request, deterministic | bursty, model-dependent | same request rejected serially, rare partial success |
| Model-dependent | no | yes | partially (whichever pool's edge is degraded) |
| Status page | n/a (local) | usually has an incident | may have none |
| Fix | disable v6 / fix path MTU on all interfaces | wait / switch model | abandon session, fresh small context |

---

Additional suggestion for the Claude Code team (beyond the June 10 list)

  1. Cap identical-request retries on connection-level failures. The client re-uploaded an identical ~680 KB body ~16 times within ~25 minutes after server-initiated closes. When the same payload is killed repeatedly post-upload, later attempts add cost without information. Detecting "uploaded fully, closed without response, N consecutive times" and surfacing "the server is repeatedly dropping this request — consider /compact in a fresh process or reducing context" would convert a silent 25-minute failure into an actionable 1-minute one.
7rhh7v89hb-rgb · 11 days ago

I think the solution is easier than we thought.

On macOS, we have the private relay always on. For some reason, it stops Claude Code. So I’ve stopped it for one day an Claude started to work straight away

paulosantos-ios · 10 days ago

Two forensic captures at the exact moment of attempt 10/10 ECONNRESET — transport verifiably clean, failure is session/payload-dependent (supports the "server drops large requests" bucket)

Environment: macOS (Apple Silicon, Darwin 25.4.0), Claude Code 2.1.201, Max plan, Wi-Fi 802.11ax (SNR 54 dB, 2.1 Gbps PHY rate), no VPN, no global IPv6 addresses (all traffic tcp4).

I set up a LaunchAgent that samples every claude process's connections every 20s (netstat + nettop per PID) and fires a deep diagnostic bundle (spaced nettop, gateway ping, 1500B don't-fragment MTU probe, fresh TLS handshake timing, DNS, status page) the moment an API error entry lands in a session transcript. That gives measurements from the exact second of failure, not after-the-fact.

Ruled out on this machine before capturing (following the buckets earlier in this thread):

  • IPv6 PMTUD black hole: no global v6 addresses at all
  • MTU black hole: 1500-byte DF pings to api.anthropic.com, 0% loss
  • VPN residue: utun routes are link-local only
  • Wi-Fi signal: SNR 54 dB
  • Idle-drop / SO_KEEPALIVE class: applied sudo sysctl -w net.inet.tcp.always_keepalive=1 net.inet.tcp.keepidle=60000 (the macOS-native equivalent of the Linux LD_PRELOAD shim posted above — no shim needed on macOS). Verified working: idle connections now survive long local tool execution. The failures below happened anyway, so keepalive is not the remaining cause on this machine.

Capture 1 (11:55 local) and capture 2 (12:01 local), both during a live Unable to connect to API (ECONNRESET) · attempt 10/10:

| Signal | Capture 1 | Capture 2 |
|---|---|---|
| Gateway RTT (local hop) | 2.75–3.02 ms, 0% loss | 2.60–3.62 ms, 0% loss |
| api.anthropic.com ICMP | avg 19 ms, 0% loss | avg 18 ms, 0% loss |
| Fresh TLS handshake to api.anthropic.com | 68 ms total (3/3 succeeded, 63–128 ms) | 83 ms total |
| 1500B DF MTU probe | pass, 0% loss | pass, 0% loss |
| status.claude.com | All Systems Operational | All Systems Operational |

Meanwhile, in the same seconds:

  • The failing session's transcript is 19.4 MB and has accumulated 15 API error entries.
  • Other Claude Code sessions on the same machine kept talking to the API normally. Same host, same route, same second — one session fails 10/10, the others work. A network fault can't select by session; payload size can.
  • The failing request's connection showed ~984 KB uploaded, then counters frozen (upload complete, no response), followed by a retry burst of ~26 new connections on sequential ports (55374–55399) — the client re-sending the same large body.

This matches @mmisztal1980's "server drops large requests" discriminator exactly: tcp4, clean transport, zero retransmit growth at failure time, full upload out, closed without an HTTP response, same request rejected serially, no status-page incident.

Repeating the earlier asks, which this data supports:

  1. Cap identical-payload retries — 10 attempts of the same ~1 MB body add cost without information once the server has dropped it repeatedly.
  2. Distinguish reset origin in the error message — "server closed the connection after receiving the request" vs "connection stalled mid-upload" point to opposite ends of the wire.
  3. Server-side correlation — this failure mode (large bodies dropped with green status page) appears to be below the alerting threshold; edge logs for large-request drops would confirm.

Happy to share the full incident logs (sampler + diagnostics) if useful. Filed via /feedback as well.

Update: filed via /feedback from the affected session — Feedback ID: 42bef73e-e462-4574-90a3-699ce52b2a77 (the transcript attached there is the failing 19.4 MB session). Notably, the /feedback submission itself went through on the first attempt from that same session — consistent with small requests passing while large ones get dropped.

Update 2 (same day, ~5h later): two more captures with the identical signature — but on a different session (13.7 MB transcript this time): gateway 2.8–3.5 ms / 0% loss, api.anthropic.com ICMP normal, fresh TLS fine, status page green, while that session's requests died with ECONNRESET. Live observation during one failure: ~95 KB request body uploaded, then both byte counters frozen (upload complete, no response) until reset. So this is not one pathological session — any large-transcript session reproduces it, and the failing body doesn't need to be huge (~95 KB–1 MB observed). Four captures, two sessions, one signature: server-side/edge drops selecting on request size, invisible on the status page.

viniciusmorgado · 5 days ago
I think the solution is easier than we thought. On macOS, we have the private relay always on. For some reason, it stops Claude Code. So I’ve stopped it for one day an Claude started to work straight away

This is not a solution. Claude should work even with Private Relay enabled, since it is an OS default setting.