anthropics / anthropics/claude-agent-sdk-typescript
Bundled cli.js fails IPv6 happy-eyeballs fallback — Vertex AI OAuth hangs on dual-stack networks
- 主要语言
- Shell
- 星标
- 1.8k
- 派生
- 226
- PR 合并指标
- 30 天内没有已合并 PR
描述
## 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
贡献指南
这个仓库没有索引到贡献指南
评估
这个 Issue 还没有评估数据。