[BUG] macOS: Local Network permission is lost on every auto-update (versioned claude.app bundle path); 20+ stale permission rows accumulate
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:
- The Local Network grant is lost on every update. The freshly installed
claude.apphas no grant, so LAN unicast from Claude's shell starts failing withsendto: No route to host/EHOSTUNREACHuntil 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. - 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
- 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.xvia the Bash tool) so the Local Network prompt fires; grant it. - Let the app auto-update (new
~/Library/Application Support/Claude/claude-code/<new-version>/claude.appis installed, old one removed). - 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)
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
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.pliston this machine contains 20 separate records forcom.anthropic.claude-code, one per versioned path (…/claude-code/2.1.72/…through2.1.247). The record for the currently running2.1.247binary hasDenyMulticast => 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.
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:
Ruled out along the way, each verified rather than assumed:
dangerouslyDisableSandbox: truereturns byte-identical failures.interface: en0), the ARP entry is resolved, and neitherbind()to the en0 address norIP_BOUND_IFon en0 changes anything.sudo killall -9 nehelperand a full reboot both leave the denial intact.tccutilcannot help, and the error message misleadskTCCServiceLocalNetworkdoes 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: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) forcom.anthropic.claude-codeyields 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:
DenyMulticast = true, alongside the versioned one. So the denial is not purely per-path.Meanwhile System Settings shows a long list of enabled rows named
claude,node,bashwith 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:
Manual registration is accepted but does not persist:
Combined with
LSBackgroundOnly = truein 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 declareNSLocalNetworkUsageDescription, so neither signing nor the usage string is the gap.Workaround for anyone landing here
Processes launched by
launchdare unaffected, so LAN commands can be routed around the denial. Minimal version: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
claudefrom 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
.gitignorehandling 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.