[BUG] macOS: silently-denied Local Network permission permanently breaks all daemon-spawned sessions (ENOTFOUND); attaching to a fleet session poisons every session in the terminal

Status Open
Reported on v2.1.217
Maintainer reply None cached
Activity 2 comments · opened Jul 22, 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?

Environment

  • Claude Code 2.1.217 (native installer, auto-updating), macOS 26 (Darwin 25.5.0)
  • Home network where the only DNS resolver is the router itself (Fritz!Box at 192.168.178.1 — a local-network address). This is the default on very common consumer routers.

What's wrong

Every daemon-spawned session (resumed from the agent overview / FleetView, pre-warmed spares, background handoffs) fails every API call with API Error: Unable to connect to API (ENOTFOUND), while directly-spawned terminal sessions work fine. Deterministic repro for a week, across 2.1.215→2.1.217.

Root cause, confirmed by decoding /Library/Preferences/com.apple.networkextension.plist:

  • The identity com.anthropic.claude-code has a macOS Local Network record with DenyMulticast=True, MulticastPreferenceSet=False — i.e. a default-deny minted without the permission prompt ever being shown.
  • It was minted when the daemon/spare architecture first ran on this machine (~Jul 16, arriving via auto-update) while the user was working over SSH/screen-share. Daemon workers are TCC-disclaimed background processes (CLAUDE_BG_TCC_DISCLAIMED=1) with no UI, so macOS could never display the one-time prompt → silent default-deny, permanent.
  • Denied workers cannot resolve any hostname when DNS goes through mDNSResponder. Notably, configuring public resolvers (1.1.1.1) does not help — resolution is blocked at the mDNSResponder policy layer for these processes regardless of resolver address (consistent with the "Failed to create Attribution Chain" behavior in #59065).
  • Directly-spawned claude is attributed to the terminal app (not gated) → works. This made the failure look random (and made --debug look like a fix, since debugging always happened in fresh foreground processes).

The "poisoning" behavior (worst part UX-wise)

  1. Fresh terminal, plain claude → in-process session → works.
  2. Open the agent overview and select any older session → the daemon claims a spare and hosts it (daemon.log: bg claimed-spare <id> (fleet)) → ENOTFOUND.
  3. The user's original working session is simultaneously handed off to the daemon (so it can keep running in the background), and any new session created afterwards is also daemon-spawned. From that moment every session in the terminal fails until the terminal is quit and Claude restarted.

So one visit to the agent overview converts a fully-working terminal into one where nothing can reach the API — with no hint why.

Why the user cannot fix it

  • The denied identity is invisible in System Settings → Privacy & Security → Local Network (the pane only lists identities with MulticastPreferenceSet=True; the "Claude" entry there is the desktop app).
  • macOS never re-prompts once a record exists; GUI-launching the app bundle triggers no prompt.
  • The plist is entitlement-gated like TCC.db: direct write as root → EPERM; sudo defaults import silently mis-scopes to root's per-user domain; a properly-scoped CFPreferencesSetValue/Synchronize (kCFPreferencesAnyUser/AnyHost) returns success but cfprefsd silently drops the write. Short of Recovery-mode SIP-off, the deny record is immutable.
  • The Local Network entitlement added in 2.1.198 enables the prompt for fresh installs, but cannot heal a pre-existing deny record.

Workaround that fixed it

Pinning the API host in /etc/hosts (160.79.104.10 api.anthropic.com): hosts entries are resolved in-process by libinfo, bypassing mDNSResponder's policy denial, and the Local Network gate doesn't block TCP to public IPs. All daemon-spawned sessions immediately started working, deny record still in place. Downside: hardcoded IP, no failover, goes stale silently.

Suggested fixes

  • Detect this state: if a daemon worker gets ENOTFOUND while a directly-spawned probe (or the parent terminal process) can resolve, surface a diagnostic pointing at the Local Network permission instead of a bare API Error.
  • claude doctor could decode the networkextension records for com.anthropic.claude-code and flag DenyMulticast=True, MulticastPreferenceSet=False explicitly.
  • Consider having daemon workers fall back to a non-mDNSResponder resolution path (or to the responsible-process attribution of the launching terminal) when resolution is policy-denied.

Related

  • #59065 (bg-pty-host workers blocked by macOS TCC attribution failure)
  • #27828 (CLI Local Network issues)
  • Changelog 2.1.198 ("Fixed no route to host for local-network hosts in macOS background agent sessions by declaring Local Network entitlements")

What Should Happen?

Daemon-spawned sessions (fleet resume, spares, background handoffs) should have the same network access as directly-spawned terminal sessions. If macOS has silently default-denied the Local Network permission for com.anthropic.claude-code, Claude Code should detect it and tell the user what is wrong (e.g. in claude doctor and instead of the bare API Error: Unable to connect to API (ENOTFOUND)), rather than failing opaquely in only some session types. Attaching to a session from the agent overview should also never degrade the sessions the user subsequently starts in that terminal.

Error Messages/Logs

UI (every daemon-spawned session, on every message):
  API Error: Unable to connect to API (ENOTFOUND)
  Unable to connect to API (ENOTFOUND) · Retrying in 3s · attempt 4/10
  (also shows "✗ Auto-update failed · Run claude doctor" — same root cause, the worker cannot resolve any hostname)

~/.claude/daemon.log at the moment old sessions were resumed from the agent overview (all failed with ENOTFOUND):
  [2026-07-22T06:25:39.169Z] [bg] bg claimed-spare 4d186336 (fleet)
  [2026-07-22T06:28:24.369Z] [bg] bg claimed-spare 15a85be4 (fleet)

Deny record in /Library/Preferences/com.apple.networkextension.plist:
  com.anthropic.claude-code | DenyMulticast = True | MulticastPreferenceSet = False   <- prompt never shown
  com.anthropic.claudefordesktop | DenyMulticast = False | MulticastPreferenceSet = True

Also in daemon.log (separate latent issue):
  [supervisor] auth: headless daemon cannot complete OAuth — run `claude auth login` to refresh
  [supervisor] auth: no token found, will re-check keychain every 30s

Steps to Reproduce

Preconditions: macOS machine whose DNS resolver is the router itself (e.g. default Fritz!Box setup: resolver = 192.168.178.1, a local-network address).

  1. Get macOS to mint a default-deny Local Network record for com.anthropic.claude-code. This happens by itself if the daemon/spare architecture's first-ever worker touches the network while nobody can answer the permission prompt — in our case the daemon first ran (arriving via auto-update) while the user was connected over SSH/screen-share. Disclaimed background workers can never display the prompt, so the deny is minted silently. Result: DenyMulticast=True, MulticastPreferenceSet=False in /Library/Preferences/com.apple.networkextension.plist.
  2. Open a fresh terminal, run plain claude, send a message → works (in-process session, attributed to the terminal app).
  3. Open the agent overview and select any past session → every message fails with API Error: Unable to connect to API (ENOTFOUND) (daemon claims a spare: bg claimed-spare <id> (fleet) in daemon.log).
  4. Switch back to the original session, or create a new one from the overview → these now fail with ENOTFOUND too (original session was handed off to the daemon; new sessions are daemon-spawned).
  5. Quit the terminal, relaunch, plain claude → works again — until step 3 is repeated. 100% reproducible.

Confirmed the mechanism by running the same session both ways: identical session resumed via the overview fails; after pinning api.anthropic.com in /etc/hosts (bypassing mDNSResponder), the same daemon-hosted sessions work with the deny record still in place.

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.214 (last version before the daemon/spare architecture first ran on this machine, Jul 16)

Claude Code Version

2.1.217 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

_No response_

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗