firebase / firebase/firebase-ios-sdk
`Crashlytics/upload-symbols` picks newest Xcode on the machine, which generates errors for Xcode 27 visible from macOS 26
- Dominant language
- C++
- Stars
- 6.7k
- Forks
- 1.8k
- Avg merge
- 2d 14h
- Merged PRs (30d)
- 72
Description
### Background
I've got an external NVMe drive from which I boot macOS 27 + Xcode 27. On switching back to my macOS 26 boot partition, I started getting 5+ of these errors pop ups and need dismissing during each release builds:
> **“Xcode-27.0.0-Beta.6” is damaged and can’t be opened. You should move it to the Trash.**
The annoying part is it means that it requires user interaction in the GUI to unblock the build process.
This is expected behavior when Xcode 27 is attempted to be launched from macOS 26. However, I was running these builds with Xcode 26.6 selected. I set claude to track down the process that was spawning this Xcode invocation and gatekeeper error modal. This is what it came back with, which seems entirely plausible.
### Description
`Crashlytics/upload-symbols` locates Xcode by asking LaunchServices for **every** app registered with bundle identifier `com.apple.dt.Xcode`, then picks the candidate with the highest `CoreSymbolicationDT.framework` bundle version and `dlopen`s `DebugSymbolsDT` / `CoreSymbolicationDT` out of it. It ignores which Xcode is actually selected (`xcode-select` / the `DEVELOPER_DIR` that Xcode itself exports into the run-script build phase).
This found the 27 Beta Xcode on the secondary boot volume, which makes `upload-symbols` load private frameworks from an Xcode that the running OS cannot use.
### Reproduction steps
1. Install two Xcodes where the newer one cannot run on the current OS — e.g. Xcode 26.6 in `/Applications` and an Xcode 27 beta on another volume (`/Volumes/macOS Beta/Applications/Xcode-27.0.0-Beta.6.app`), booted into macOS 26.6.2.
2. `sudo xcode-select -s /Applications/Xcode-26.6.0.app/Contents/Developer`.
3. Archive an app whose target has the standard Crashlytics `run` / `upload-symbols --build-phase` script phase.
**Expected:** `upload-symbols` uses the selected Xcode (`DEVELOPER_DIR` / `xcode-select -p`) and uploads dSYMs.
**Actual:** it loads frameworks from the Xcode 27 beta, Gatekeeper blocks the load with the "damaged" modal, and the build phase blocks until a human dismisses it.
Evidence that `upload-symbols` is the process holding the beta Xcode open:
```
$ lsof +D "/Volumes/macOS Beta/Applications/Xcode-27.0.0-Beta.6.app"
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
upload-sy 21363 jmartin txt REG 1,30 776672 38894931 .../Xcode-27.0.0-Beta.6.app/Contents/SharedFrameworks/DebugSymbolsDT.framework/Versions/A/DebugSymbolsDT
upload-sy 21363 jmartin 5r REG 1,30 776672 38894931 .../Xcode-27.0.0-Beta.6.app/Contents/SharedFrameworks/DebugSymbolsDT.framework/Versions/A/DebugSymbolsDT
$ ps -o pid,ppid,command -p 21363
PID PPID COMMAND
21363 21362 .../firebase-ios-sdk/Crashlytics/upload-symbols --build-phase
# 21362 = /bin/sh .../firebase-ios-sdk/Crashlytics/run
```
The candidate list LaunchServices hands it, in order:
```
$ mdfind "kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode'"
/Volumes/macOS Beta/Users/jmartin/.Trash/Xcode-27.0.0-Beta.5.app
/Volumes/macOS Beta/Applications/Xcode-27.0.0-Beta.6.app
/Applications/Xcode-26.6.0.app # <- the selected one
$ xcode-select -p
/Applications/Xcode-26.6.0.app/Contents/Developer
```
And the versions it compares — the beta wins:
```
Xcode 27.0b6 CoreSymbolicationDT CFBundleVersion = 64578.87
Xcode 26.6 CoreSymbolicationDT CFBundleVersion = 64576.2
```
Contributor guide
Research direction
Start at Crashlytics/upload-symbols and the standard Crashlytics run / upload-symbols --build-phase entry points. Trace how LaunchServices candidates are selected, then verify the selected DEVELOPER_DIR or xcode-select -p is used instead. Done means the build phase loads frameworks from the selected Xcode and uploads dSYMs without opening an incompatible Xcode.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- macos
- Domain
- build-system, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 66/100