anthropics / anthropics/claude-agent-sdk-typescript

Bundled cli.js fails IPv6 happy-eyeballs fallback — Vertex AI OAuth hangs on dual-stack networks

Offen
#294 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
bug
Vorherrschende Sprache
Shell
Sterne
1.8k
Forks
226
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

## Bug Report

### Environment
- SDK version: 0.2.109 (cli.js), also reproduced on 0.2.87
- Node.js: v24.10.0 (also affects v18, v20, v22)
- Platform: macOS 15.4 (arm64)
- Backend: Vertex AI (`CLAUDE_CODE_USE_VERTEX=1`)

### Symptom

`query()` returns `result` events with `is_error: true` and an empty error reason:

```
API Error: request to https://oauth2.googleapis.com/token failed, reason:
```

Sessions list, calendar, and the UI all work. Only chat messages fail — the OAuth token refresh to Google hangs.

### Root Cause

The bundled `cli.js` includes undici as its HTTP client. On networks where:

1. A **Tailscale** interface provides an IPv6 ULA address (`fd7a:115c:a1e0::/48`)
2. DNS returns both A (IPv4) and AAAA (IPv6) records for `oauth2.googleapis.com`
3. IPv6 is **not routable** to Google (returns `EHOSTUNREACH`)

Undici's `autoSelectFamily` (happy-eyeballs, RFC 8305) fails to fall back to IPv4. Node's native `https` module handles this correctly, but undici does not.

### Reproduction

```bash
# 1. Have Tailscale running (provides fd7a:: IPv6 ULA by default)
# 2. Be on a network where IPv6 can't route to Google
# 3. Verify the conditions:
dig oauth2.googleapis.com AAAA +short # returns IPv6 address
ifconfig | grep "inet6.*fd7a" # Tailscale ULA present

# 4. Verify undici fails but Node https succeeds:
node -e "
fetch('https://oauth2.googleapis.com/tokeninfo', { signal: AbortSignal.timeout(5000) })
.then(r => console.log('OK:', r.status))
.catch(e => console.log('FAIL:', e.cause?.code || e.message))
"
# Output: FAIL: ETIMEDOUT

node -e "
require('https').get('https://oauth2.googleapis.com/tokeninfo', r => {
console.log('OK:', r.statusCode)
r.resume()
}).on('error', e => console.log('FAIL:', e.message))
"
# Output: OK: 400 (connects fine via IPv4 fallback)
```

### What doesn't work

| Approach | Why it fails |
|----------|-------------|
| `NODE_OPTIONS=--dns-result-order=ipv4first` | Only changes ordering — undici still attempts IPv6 from the results |
| `/etc/hosts` with IPv4-only entry | System still returns AAAA from upstream DNS |
| macOS PF firewall blocking IPv6 TCP | cli.js bypasses packet filter |
| `networksetup -setv6off` | DNS still returns AAAA records |

### Workarounds that work

1. **Local DNS resolver** (unbound/dnsmasq) that strips AAAA records for `oauth2.googleapis.com`
2. **Preload script** that patches `dns.lookup` to force `family: 4`, loaded via `NODE_OPTIONS=--require=`

### Proposed fix

Any of:

1. Set `autoSelectFamilyAttemptTimeout: 250` on the undici Agent used for OAuth requests — makes IPv6 fail fast
2. Set `autoSelectFamily: false` — skips dual-stack entirely, uses whatever `dns.lookup` returns first
3. Respect `NODE_OPTIONS=--dns-result-order=ipv4first` by configuring undici's lookup to use Node's `dns.lookup` with the default result order
4. Use Node's native `https` module instead of undici for the OAuth token refresh (it handles fallback correctly)
5. Rebuild with a newer Node version that includes the `autoSelectFamily` rework ([nodejs/node@267439f](https://github.com/nodejs/node/commit/267439fc34))

### Related upstream issues

- [nodejs/node#48145](https://github.com/nodejs/node/issues/48145) — RFC 8305 implementation discussion
- [PostHog/posthog#51661](https://github.com/PostHog/posthog/issues/51661) — identical undici ETIMEDOUT symptom

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.