[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

Status Open
Reported on v2.1.238
Maintainer reply None cached
Activity 0 comments · opened Aug 21, 2026

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).

  1. Set up a local dev hostname the standard macOS way:
  • echo '127.0.0.1 myapp.test' | sudo tee -a /etc/hosts
  • brew install dnsmasq, echo 'address=/.test/127.0.0.1' > $(brew --prefix)/etc/dnsmasq.conf, start it
  • sudo bash -c 'echo "nameserver 127.0.0.1" > /etc/resolver/test'
  1. Serve anything over HTTPS on myapp.test and add it as an MCP server: claude mcp add -s local -t http myapp https://myapp.test/mcp
  2. With the VPN disconnected, run claude mcp get myapp → connects.
  3. Connect the VPN, then run claude mcp get myappENOTFOUND: getaddrinfo ENOTFOUND myapp.test.
  4. In the same shell, confirm the OS still resolves it: dscacheutil -q host -a name myapp.test and curl -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

View original on GitHub ↗