Fleet changelogs · dev.ecs0.net
rdmsm4x-changelog-20260831-1903-xcode-location-services-repair

rdmsm4x — Xcode Location Services toggle repair + Xcode bundle-ID dedupe

Host: rdmsm4x (macOS 27.0, 26A5421a) · Window: 2026-08-31 18:47–19:03 EDT Summary: The Location Services switches labelled "Xcode"/"Xcode-beta" reverted on every attempt because locationd refused the write. Root cause identified from locationd's own log and the clients.plist schema; three duplicate Xcode bundles sharing one bundle ID were archived; location confirmed working at runtime from both test runners.

Root cause

locationd logged, at the exact moments of the user's two toggle attempts:

18:50:23.804  locationd [Core:Client] #Spi, Must provide a bundle identifier
                or bundle path for which to set location authorization status
18:50:25.471  (same)

/var/db/locationd/clients.plist keys clients three ways, and the schema differs:

Key type Count (before) Fields Can hold Authorized?
:i bundle ID 66 BundleId, BundlePath, Requirement yes — 46 authorized
:p bundle path 20 BundlePath, Requirement yes
:e executable path 6 ClientStorageToken, Executable, Registered, Requirement no Authorized field on any :e row in the DB

The two switches were backed by :e rows for …/XcodeDefault.xctoolchain/usr/libexec/swift/pm/swiftpm-testing-helper (one under Xcode.app, one under Xcode-beta.app) — SwiftPM's test helper, a bare Mach-O with no enclosing bundle. System Settings labels them from the enclosing app, so they read as "Xcode"/"Xcode-beta". With neither a bundle identifier nor a bundle path on the record, locationd rejected the authorization write and the UI snapped back. Same class affects 3 headless-Chrome binaries (Playwright/Puppeteer) and navd.

Ruled out with evidence: SIP enabled but clients.plist carries no restricted flag and no xattrs; Location Services globally enabled (=1); zero configuration profiles mention location; all three recorded cdhash requirements matched their on-disk binaries exactly (not a stale-signature revocation).

Changes made

  1. Archived duplicate Xcode bundles (moved, not deleted; 7.1 GB):
    • /Applications/Xcode.app (26.6, 17F112) → /Users/richh/Archive/xcode-dupes-20260831/
    • /Applications/Xcode-27-beta5.app (27.0, 27A5237k) → same
    • Kept /Applications/Xcode-beta.app (27.0, 27A5237k — byte-identical build to the beta5 copy, and the xcode-select target).
    • All three had declared CFBundleIdentifier = com.apple.dt.Xcode. Unregistered the archived copies with lsregister -u; re-registered the keeper.
  2. Edited /var/db/locationd/clients.plist (locationd stopped, edited, restarted):
    • Dropped the now-stale :e row pointing into the archived /Applications/Xcode.app.
    • Added BundlePath + Authorized=True to the surviving Xcode-beta :e row.
    • Synthesised a :p/Applications/Xcode-beta.app row with Authorized=True.
    • All three edits survived locationd's restart and a subsequent minute of runtime.
  3. ~/scripts/preauthorize_dev_permissions.zsh — removed 4 DEV_BINS entries pinned to the archived /Applications/Xcode.app (xcodebuild, xctest, lldb, lldb-rpc-server). zsh -n clean.

Verification

Built a throwaway SwiftPM package that creates a real CLLocationManager, calls requestWhenInUseAuthorization() + startUpdatingLocation(), and waits on the delegate.

Runner Status before Result
XCTest (xctest) notDetermined GOT_FIX, accuracy 35.0 m → authorizedAlways
swift-testing, isolated (--disable-xctest, via swiftpm-testing-helper) notDetermined GOT_FIX, accuracy 35.0 m → authorizedAlways

Toolchain intact after the archive: xcode-select -p = Xcode-beta, xcodebuild -version = Xcode 27.0 (27A5237l), swift --version = 6.4.

Note on attribution: a first probe read CLLocationManager.authorizationStatus without issuing a request and returned notDetermined — that check measures registration, not capability, and was misleading. The grant materialises at request time in both runners, so the functional capability is present; the plist edit is not independently proven to be what supplied it.

Not verified — needs the owner's eyes

Whether the System Settings switch itself now stays on. The surviving row is still :e-keyed, so the switch may remain inert even though location works; the added :p row may or may not surface a toggleable entry. Requires opening System Settings → Privacy & Security → Location Services.

How to undo

# restore the locationd database
sudo cp ~/dev/fleet/snapshots/locationd/clients.plist.20260831-185402 \
        /var/db/locationd/clients.plist
sudo chown _locationd:_locationd /var/db/locationd/clients.plist
sudo chmod 644 /var/db/locationd/clients.plist
sudo killall locationd

# restore the Xcode bundles
sudo mv /Users/richh/Archive/xcode-dupes-20260831/Xcode.app /Applications/
sudo mv /Users/richh/Archive/xcode-dupes-20260831/Xcode-27-beta5.app /Applications/

# restore the preauthorize script
cp /Users/richh/Archive/xcode-dupes-20260831/preauthorize_dev_permissions.zsh.bak \
   ~/scripts/preauthorize_dev_permissions.zsh

Artifacts

Outstanding owner actions

  1. Confirm the System Settings switch behaviour (above).
  2. Decide whether Xcode 26.6 should return to /Applications — it is the last non-beta and may matter for the two Intel hosts (rdmpw3265m / rdmpw3275m, macOS 26.7). It is archived, not deleted; one mv restores it, at the cost of reintroducing the bundle-ID collision.