[BUG] macOS: Local Network permission is lost on every auto-update (versioned claude.app bundle path); 20+ stale permission rows accumulate

Status Open
Reported on v2.1.246
Maintainer reply None cached
Activity 8 comments · opened Aug 28, 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, the desktop app's Claude Code helper is installed at a versioned bundle path:

~/Library/Application Support/Claude/claude-code/<version>/claude.app

macOS's Local Network privacy system registers the grant against that specific bundle path. Every auto-update installs a new versioned bundle and deletes the old one, with two consequences:

  1. The Local Network grant is lost on every update. The freshly installed claude.app has no grant, so LAN unicast from Claude's shell starts failing with sendto: No route to host / EHOSTUNREACH until the user notices, finds the new prompt or row, and re-toggles it. The gateway and internet stay reachable (that is how the Local Network gate works), VPN/tunnel interfaces (e.g. Tailscale utun) are unaffected, and every other app on the machine keeps LAN access - which makes this very easy to misdiagnose as a router or network fault. It can also strike mid-session, at the moment the helper context changes during an update.
  2. Stale permission rows accumulate. System Settings -> Privacy & Security -> Local Network on this machine currently shows 20+ rows all named claude.app, most with a generic blank icon because the bundle behind them no longer exists. Only ONE versioned bundle is actually on disk right now (.../claude-code/2.1.246/claude.app). There is no per-row delete UI in System Settings, so the list only ever grows.

The code signing identity is stable across versions, so the churn is purely the install path:

Identifier=com.anthropic.claude-code
Authority=Developer ID Application: Anthropic PBC (Q6L2SF6YDW)
TeamIdentifier=Q6L2SF6YDW
flags=0x10000(runtime)

Earlier on this machine the permission registry showed the grant bound to .../claude-code/2.1.217/claude.app while the installed app had already moved on many versions - the grant simply never follows the update.

Related but distinct issues, for cross-reference:

  • #74378 - the bare un-bundled CLI binary cannot register for Local Network at all (missing Info.plist). This report is the complementary desktop-app case: the helper IS a proper signed bundle and registers fine, but its identity-by-path churns every release.
  • #88146 - desktop app SSH EHOSTUNREACH on the local subnet; plausibly the same underlying permission state.
  • #74831 (closed, area:packaging) - permission prompts showing the version number instead of "Claude Code"; a cosmetic symptom of the same versioned-bundle design.
  • #80072 - silently-denied Local Network permission breaking daemon-spawned sessions.

Steps to Reproduce

  1. On macOS 15+ (observed on macOS 26.x), run the Claude desktop app with Claude Code, and have a session touch any LAN host (e.g. ssh 192.168.x.x via the Bash tool) so the Local Network prompt fires; grant it.
  2. Let the app auto-update (new ~/Library/Application Support/Claude/claude-code/<new-version>/claude.app is installed, old one removed).
  3. Have a session touch the same LAN host again.

Result: No route to host / EHOSTUNREACH from Claude's processes only; a NEW Local Network row/prompt appears for the new bundle; the old row remains as a dead entry. Repeat per release - after months of updates the panel holds 20+ dead claude.app rows.

Expected: the Local Network grant survives updates (stable helper location, or LAN traffic routed through a stable parent/XPC helper so the grant binds to something that persists), and dead rows are not left behind.

Suggested Direction

Since Identifier and TeamIdentifier are already stable, keeping the permission-bearing bundle at a stable, versionless path (or holding the Network-framework/LAN-touching activity in the stable parent app or a stable helper bundle, as sibling issue #74378 also proposes for the CLI) should let the grant persist across updates and stop the row accumulation.

Environment

  • macOS 26.6.2 (build 25G83), Apple Silicon (M3)
  • Claude desktop app with Claude Code helper 2.1.246 (~/Library/Application Support/Claude/claude-code/2.1.246/claude.app)
  • Observed repeatedly since at least May 2026 across many app versions (grant re-lost after updates; independently re-diagnosed on this machine at least three times)

View original on GitHub ↗

3 Comments

github-actions[bot] · 1 month ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/86026
  2. https://github.com/anthropics/claude-code/issues/84234
  3. https://github.com/anthropics/claude-code/issues/86638

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

ThorNeoV · 1 month ago

Confirming this on another machine, with two additional data points from a diagnosis session today (macOS 26 / Darwin 25.6.0, desktop app helper claude-code/2.1.247):

1. Re-toggling the visible rows + fully restarting the app does NOT recover LAN access. All rows named Claude/claude in System Settings → Privacy & Security → Local Network were switched on, the app was fully quit and relaunched (verified via process start times) — LAN unicast still failed with EHOSTUNREACH/100% ICMP loss while the gateway and internet stayed reachable. So the standard user remedy for this class of problem doesn't work here.

2. The live binary is explicitly recorded as denied while the UI shows "allowed". /Library/Preferences/com.apple.networkextension.plist on this machine contains 20 separate records for com.anthropic.claude-code, one per versioned path (…/claude-code/2.1.72/… through 2.1.247). The record for the currently running 2.1.247 binary has DenyMulticast => true, i.e. macOS registered a denial for the fresh bundle path, while the toggles the user can see correspond to stale older-version records. The UI state and the enforced state disagree, so from the user's side the setting looks correctly enabled ("a default PC setting which is enabled") while every Claude session on the machine is LAN-blind.

Real-world cost today: ~2 hours misdiagnosing this as an SSH/network fault across two sessions during on-site IT work (symptoms perfectly mimic a dead host: ARP resolves, ping/TCP fail, other apps on the same machine connect fine). Only a reboot (to flush nehelper policy state and re-trigger the OS prompt) appears to recover it once the deny is recorded.

A stable (non-versioned) helper bundle path, or migrating the grant on update, would fix both the permission loss and the row accumulation.

evgn-k · 1 month ago

Confirming on a third machine, with a decisive test that isolates the cause and the exact plist records behind it.

Environment: macOS 26.6.2 (25G83), desktop app helper claude-code/2.1.258.

Decisive test: it is the responsible process, nothing else

Same script, same interpreter, same uid, seconds apart — only the launcher differs:

via launchd (launchctl submit):   192.168.x.10:22    OK
                                  192.168.x.20:5001  OK
via Claude Code (Bash tool):      192.168.x.10:22    [Errno 65] No route to host
                                  192.168.x.20:5001  [Errno 65] No route to host

Ruled out along the way, each verified rather than assumed:

  • Not the Bash sandbox — dangerouslyDisableSandbox: true returns byte-identical failures.
  • Not routing or interface selection — the failure precedes routing. A valid route exists (interface: en0), the ARP entry is resolved, and neither bind() to the en0 address nor IP_BOUND_IF on en0 changes anything.
  • Not uid — uid 501 in both cases.
  • Not Tailscale — "Allow local network access" is on; tailnet peers stay reachable throughout (they traverse the tunnel).
  • Not stale daemon state — sudo killall -9 nehelper and a full reboot both leave the denial intact.

tccutil cannot help, and the error message misleads

kTCCServiceLocalNetwork does not exist in TCC.db at all — not for any application. Both of these fail even under sudo, because the service is not a TCC service:

$ sudo tccutil reset LocalNetwork
tccutil: Failed to reset LocalNetwork
$ sudo tccutil reset kTCCServiceLocalNetwork
tccutil: Failed to reset kTCCServiceLocalNetwork

Worth stating explicitly in any workaround guidance — it reads like a permissions problem and sends people looking in the wrong place.

The records, and where UI and enforcement diverge

Parsing /Library/Preferences/com.apple.networkextension.plist (NSKeyedArchiver) for com.anthropic.claude-code yields 16 records:

| Records | Application path | Flags |
|---|---|---|
| 8 | ~/.local/share/claude/versions/2.1.172 … 2.1.214 (pre-bundle CLI) | MulticastPreferenceSet |
| 5 | …/claude-code/2.1.229 … 2.1.247/claude.app/… | MulticastPreferenceSet |
| 1 | …/claude-code/2.1.255/claude.app/… | MulticastPreferenceSet, DenyMulticast = true |
| 2 | bundle id, no path | one carries DenyMulticast = true |

Two details that differ from the original report and may matter for diagnosis:

  1. A path-less record for the bundle id also carries DenyMulticast = true, alongside the versioned one. So the denial is not purely per-path.
  2. The currently running version (2.1.258) has no record at all. The newest record is 2.1.255. The denial is inherited rather than freshly recorded, which is consistent with the prompt never firing for the new path.

Meanwhile System Settings shows a long list of enabled rows named claude, node, bash with no icons — the stale ones. The user sees fourteen switches turned on while the enforced state is a denial.

Why no icons, and why no prompt

The nested bundle is not registered with LaunchServices at all:

$ lsregister -dump | grep -c "claude-code"
0
$ lsappinfo list | grep -i claude      # only /Applications/Claude.app and its helpers

Manual registration is accepted but does not persist:

$ lsregister -f "~/Library/Application Support/Claude/claude-code/2.1.258/claude.app"
$ lsregister -dump | grep -c "claude-code"
0

Combined with LSBackgroundOnly = true in that bundle's Info.plist, macOS has no foreground app to present the prompt to — which is exactly the root cause #91373 documents. The bundle is correctly signed (Developer ID Application: Anthropic PBC, Q6L2SF6YDW) and does declare NSLocalNetworkUsageDescription, so neither signing nor the usage string is the gap.

Workaround for anyone landing here

Processes launched by launchd are unaffected, so LAN commands can be routed around the denial. Minimal version:

launchctl submit -l my.label -o out.txt -e err.txt -- /bin/sh -c 'your-command'

A wrapper that blocks on a FIFO until completion and passes stdout, stderr and the exit code through works well enough for day-to-day use. Two alternatives that also work: launching claude from Terminal.app (it inherits Terminal's grant), or addressing hosts over a VPN/tunnel address instead of their LAN address.

None of these fix MCP servers spawned by the desktop app, which need stdio and inherit the denial.

On triage

#91373 contains the full root-cause analysis with a repro and is labelled bug / has repro, yet was closed NOT_PLANNED the same day with no comment. It seems worth reopening or linking here.

Also, the three duplicates proposed by the bot on this issue (#86026, #84234, #86638) are all about .gitignore handling and unrelated — this issue should not be auto-closed against them.

Real-world cost on this machine: a full day misdiagnosed as a network fault, since the symptom mimics a dead host precisely — ARP resolves, the route is valid, other applications on the same machine connect fine, and the same commands succeed from the user's own terminal.

Showing cached comments. Read the full discussion on GitHub ↗