[BUG] macOS: hostnames from /etc/hosts and /etc/resolver/* fail with getaddrinfo ENOTFOUND while a VPN's DNS is active, breaking local HTTP MCP servers
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
On macOS, when a corporate VPN is connected, Claude Code fails to resolve local development hostnames that the rest of the OS resolves fine, so HTTP MCP servers on those hostnames cannot connect or authenticate:
$ claude mcp login devpost
Starting authentication for "devpost"…
Couldn't complete authentication for "devpost": SDK auth failed: getaddrinfo ENOTFOUND devpost.test
$ claude mcp get devpost
Status: ✘ Failed to connect
Issue: ENOTFOUND: getaddrinfo ENOTFOUND devpost.test
At the same moment, in the same shell, every OS-resolver client resolves the name correctly:
$ dscacheutil -q host -a name devpost.test → ip_address: 127.0.0.1
$ node -e "require('dns').lookup('devpost.test',(e,a)=>console.log(e||a))" → 127.0.0.1
$ bun -e "console.log(await Bun.dns.lookup('devpost.test'))" → 127.0.0.1
$ curl -o /dev/null -w '%{http_code} %{remote_ip}\n' https://devpost.test/mcp → 401 127.0.0.1
The hostname is served three ways, all of which Claude Code appears to skip:
/etc/hosts:127.0.0.1 devpost.test/etc/resolver/test:nameserver 127.0.0.1(macOS per-domain scoped resolver)- dnsmasq on 127.0.0.1 with
address=/.test/127.0.0.1(verified answering over UDP and TCP, v4 and v6)
The VPN is the variable. With the VPN disconnected, the same binary and the same config connect fine — even though the active nameservers are then 8.8.8.8/8.8.4.4, which cannot answer .test. So in that state resolution must be coming from /etc/hosts or the scoped resolver. Connect the VPN (which installs its DNS-filtering resolvers as the system nameservers, and rewrites /etc/resolv.conf to point at them) and every lookup of devpost.test becomes ENOTFOUND, while getaddrinfo clients keep working.
Timeline from my machine today, correlating the VPN client's own log with Claude Code MCP calls:
12:29:21 VPN: Disconnected
12:56–12:59 devpost.test MCP tool calls succeed
13:05:37 VPN: connect, "using ThreatBlock config: (enabled: true, dnsServers: [...])"
13:05:39 VPN: Connected
13:19 onward every devpost.test lookup → ENOTFOUND
(now) VPN disconnected again → claude mcp get devpost → ✔ Connected
It is not connectivity or TLS — only name resolution. Pointing an MCP server at a publicly resolvable name that maps to loopback (https://devpost.test.localtest.me/mcp, which public DNS answers as 127.0.0.1) connects even with the VPN up, failing later at ERR_TLS_CERT_ALTNAME_INVALID as expected. Likewise https://127.0.0.1/mcp reaches the server. Only names that require /etc/hosts or a scoped resolver fail.
Related, and probably the same underlying design: #78529 and #81521 both describe the embedded Bun resolver reading /etc/resolv.conf and querying nameservers itself rather than going through the OS resolver. On macOS that file is explicitly not authoritative — it even carries the comment "This file is not consulted for DNS hostname resolution … by most processes on this system" — and bypassing getaddrinfo there also loses /etc/hosts and all of /etc/resolver/*. This report is the user-visible macOS symptom of that: local dev hostnames break the moment a VPN's DNS becomes the resolv.conf nameserver.
Expected: Claude Code resolves hostnames the way the rest of macOS does — /etc/hosts first, then scoped resolvers from /etc/resolver/*, then system nameservers — so .test-style local development hosts keep working regardless of VPN state.
Steps to Reproduce
Requires macOS and a VPN client that installs its own DNS servers (mine is a corporate NordLayer profile with ThreatBlock enabled; any VPN that pushes resolvers whose upstream returns NXDOMAIN for your local TLD should do).
- Set up a local dev hostname the standard macOS way:
echo '127.0.0.1 myapp.test' | sudo tee -a /etc/hostsbrew install dnsmasq,echo 'address=/.test/127.0.0.1' > $(brew --prefix)/etc/dnsmasq.conf, start itsudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/test'
- Serve anything over HTTPS on
myapp.testand add it as an MCP server:claude mcp add -s local -t http myapp https://myapp.test/mcp - With the VPN disconnected, run
claude mcp get myapp→ connects. - Connect the VPN, then run
claude mcp get myapp→ENOTFOUND: getaddrinfo ENOTFOUND myapp.test. - In the same shell, confirm the OS still resolves it:
dscacheutil -q host -a name myapp.testandcurl -v https://myapp.test/both succeed.
Environment
- Claude Code 2.1.238 (native install,
~/.local/share/claude/versions/); also reproduced on 2.1.237, 2.1.236 and 2.1.233, so this is not a recent regression - macOS, Darwin 25.5.0, arm64
- VPN: NordLayer with ThreatBlock DNS filtering enabled
- dnsmasq 2.x via Homebrew,
/etc/resolver/test→ 127.0.0.1