anthropics / anthropics/claude-code

[BUG] ECONNRESET on direct ISP path: Anthropic ingress resets TLS 1.3 handshakes carrying X25519MLKEM768; classical key share or VPN is 12/12 (re-open of #74767, still on 2.1.270)

Open
#94,225 1 comment 0 reactions 0 assignees View on GitHub
api:anthropic area:networking bug has repro platform:macos
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

### What's Wrong?

Claude Code fails with `API Error: Connection dropped (ECONNRESET)` in windows of minutes to hours on my ISP path (Movistar / Telefónica, Spain, home fibre and office, Wi-Fi and Ethernet). curl, the browser and claude.ai are clean at the same moment. A VPN makes it go away completely.

I isolated it to the TLS 1.3 key share: the Anthropic ingress at `160.79.104.10:443` resets handshakes whose ClientHello carries `X25519MLKEM768` (about 1.7 KB, two TCP segments) on this path, and never resets a classical `X25519:P-256` ClientHello (about 300 B, one segment). Claude Code's client (Bun / BoringSSL) always offers `X25519MLKEM768` and has no setting to disable it, so it is the only client on the machine that fails. curl (LibreSSL, no PQ share) and Node with `ecdhCurve` restricted to classical never fail.

This is the same isolation as the last human comment on #74767 (another user, Argentina, Node 24+/OpenSSL 3.5), which was auto-closed for inactivity on 2026-09-04. Support ticket 215475788280207 (2026-09-04) has had no reply in 10 days. It is still reproducible today on 2.1.270.

**Same interface, same minute, alternating only the key-share configuration** (12 fresh TLS 1.3 POSTs to `api.anthropic.com`, Python 3 / OpenSSL 3.6.3):

| time | groups offered | ok | reset |
|---|---|---|---|
| 10:10:01 | X25519:P-256 | 12 | 0 |
| 10:10:05 | default incl. X25519MLKEM768 | 5 | 7 |
| 10:10:10 | X25519:P-256 | 12 | 0 |
| 10:10:14 | default incl. X25519MLKEM768 | 6 | 6 |

**Path is the variable, not the client.** Same PQ ClientHello, same minute (2026-09-14, Claude Code 2.1.270, Bun 1.4.3):

| path | ok | reset |
|---|---|---|
| en0 direct (Movistar), bound with IP_BOUND_IF | 5 | 7 |
| ipsec0 (CyberGhost VPN) | 12 | 0 |

Across two weeks the direct path gives 3/9, 4/8, 5/7, 4/8 in failing windows and 12/12 through the VPN every time. Control hosts with the same PQ ClientHello on the direct path: github.com 12/12, www.google.com 12/12.

**A classical-TLS proxy proves the cause.** A local Caddy reverse proxy with `GODEBUG=tlsmlkem=0` (Go omits the ML-KEM share) forwarding to `https://api.anthropic.com`, with the CLI pointed at it via `ANTHROPIC_BASE_URL`: 12/0 on every cycle while the direct PQ probe next to it gave 4/8 and 5/7. I cannot keep that workaround because Remote Control refuses to start when `ANTHROPIC_BASE_URL` is not `api.anthropic.com`, and the desktop app overrides the variable in every session it launches.

Packet capture of a reset: TCP handshake completes, the two ClientHello segments are ACKed, the server sends one packet and then RST about 1 ms later. Capture and probe scripts available on request.

### What Should Happen?

Claude Code should connect on the same network where curl, the browser and claude.ai work. Either the ingress accepts two-segment ClientHellos on this path, or Claude Code exposes a way to offer only classical key shares (the equivalent of Node's `--tls-keylog`-free `ecdhCurve` / Go's `GODEBUG=tlsmlkem=0`), so a paying customer does not need a permanent VPN.

### Error Messages/Logs

```
[ERROR] API error (attempt 11/11): undefined Connection error.
[ERROR] Connection error details: code=ECONNRESET, message=The socket connection was closed unexpectedly.
[ERROR] API error x-client-request-id=8dc5ace8-1362-4c11-9306-dd5d2496a1c9
```

### Steps to Reproduce

1. On an affected path, run Claude Code with the VPN off: `claude "what is 2+2"` fails with ECONNRESET after retries.
2. In the same minute, run 12 TLS 1.3 POSTs to `api.anthropic.com:443` with OpenSSL 3.5+ default groups (includes X25519MLKEM768): some are reset by the server.
3. Repeat with `SSL_CTX_set1_groups_list("X25519:P-256")`: 12/12 ok.
4. Repeat step 2 through any VPN: 12/12 ok.

### What I ruled out

MTU (1500, DF pings at 1472/1400/1200 clean), IPv6 (ISP gives none), macOS firewall, system proxies, third-party network extensions, memory pressure, sleep/wake, Wi-Fi vs Ethernet, two different networks, client versions 2.1.252 through 2.1.270.

### Related

#74767 (auto-closed, same isolation in its last comment), #93031, #87424, #90764, #91970, #90964, #92227 (same symptom: only Claude Code fails, VPN or hotspot fixes it). #89608 is a different mechanism (keep-alive reuse past the 400 s edge idle timeout) and does not explain fresh-socket handshake resets.

### Claude Code Version

2.1.270 (native install, Bun 1.4.3)

### Platform

Claude Max subscription (OAuth, no API key)

### Operating System

macOS 26 (Darwin 25.5.0), Apple Silicon

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reproducing the direct-path comparison in the issue: run the Claude command and the 12 TLS 1.3 POSTs with default groups versus X25519:P-256, then repeat through a VPN. Inspect Claude Code's Bun/BoringSSL connection setup and the ANTHROPIC_BASE_URL behavior. Done means direct connections work without a VPN, either because the ingress accepts the ClientHello or because Claude Code offers a classical-key-share option.

Written by the indexing model from the issue text.

Assessment

Tech stack
bun, macos, python
Domain
api, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.