ITMS-90111 wasn't my Xcode — it was my beta macOS build machine
App Store review bounced an iOS build with ITMS-90111: Unsupported SDK or Xcode version — App submissions must use the latest Xcode and SDK Release Candidates.
That message is a lie. I was on the latest submittable Xcode. Chasing what it actually meant cost me two rejected builds and one very convincing wrong theory. Here's the whole trail, including the dead end, because the dead end is where most people (me included) stop.
The red herring: "your Xcode is too old"
The Mac was running Xcode 26.6 (17F113) with the iOS 26.5 SDK. That's the newest shipping Xcode. The next one up — Xcode 27 — is still in beta, and you can't submit App Store builds with a beta toolchain. So there was literally nothing newer to install. The rejected build had already used the latest submittable everything.
When the error text and the evidence disagree this hard, stop trusting the error text and go look at the actual binary.
Reading the archive
I unpacked the .ipa and checked the platform stamps on every Mach-O:
$ vtool -show-build Payload/App.app/App
platform IOS
minos 16.0
sdk 26.5
$ vtool -show-build Payload/App.app/Frameworks/libswiftCompatibilitySpan.dylib
platform IOS
sdk 26.4
All platform IOS, all released SDKs. Nothing beta, nothing exotic. The binary's platform stamps were fine. So the "unsupported SDK" text was pointing at something the SDK version couldn't explain.
The detour that looked like the answer
I was building headlessly — xcodebuild archive driven over SSH from another machine — and the archive was dying in the CopySwiftLibs phase:
/usr/bin/codesign --force --sign <IDENTITY> .../Frameworks/libswiftCompatibilitySpan.dylib
libswiftCompatibilitySpan.dylib: replacing existing signature
libswiftCompatibilitySpan.dylib: errSecInternalComponent
errSecInternalComponent from codesign is a real, nasty bug and it is worth knowing: the signing identity's private key lives in the login keychain, and reaching it requires being inside the GUI (Aqua) security session. An SSH session isn't. Unlocking the keychain and setting the partition list — the internet's stock advice — does not fix it:
# necessary for CI in general, but did NOT clear errSecInternalComponent:
security unlock-keychain -p "$PW" ~/Library/Keychains/login.keychain-db
security set-key-partition-list -S apple-tool:,apple:,codesign: \
-s -k "$PW" ~/Library/Keychains/login.keychain-db
What fixes it is running the whole build inside the logged-in GUI session:
sudo launchctl asuser 501 sudo -u youruser -i /path/to/build.sh
(501 is the console user's UID.) With that wrapper, codesign reached the key, the embedded Swift dylib signed cleanly, exportArchive produced a valid .ipa, and altool uploaded with no errors.
So I fixed the signing, uploaded a clean build, resubmitted — and felt clever.
Then Build 2 got the exact same rejection
ITMS-90111 again. A build that was signed correctly, uploaded with zero errors, and showed a valid signature in App Store Connect. That killed the signing theory dead: a malformed signature was never the cause, because the correctly signed build was rejected identically. When your fix "works" and the symptom is byte-for-byte unchanged, your fix wasn't the fix.
Back to first principles: what is stamped on the binary that I hadn't checked? Not the SDK — the build machine.
$ plutil -extract BuildMachineOSBuild raw App.app/Info.plist
26A5368g
$ sw_vers
ProductVersion: 27.0
BuildVersion: 26A5368g
26A5368g is a beta macOS. Apple's ingestion pipeline reads BuildMachineOSBuild and rejects binaries produced on an unreleased OS — and it reports that rejection as ITMS-90111, the same code it uses for a too-old Xcode. Same lossy code, completely different cause. The Apple Developer Forums have a long thread (763451) of people hitting exactly this after opting their daily driver into a macOS beta.
Nothing about my sources, my Xcode, my SDK, or my signing was ever wrong. The machine I built on was running an OS Apple won't accept builds from.
What actually fixes it
Build on a released macOS. Options, worst to best for a machine you use daily:
- Downgrade the Mac off the beta — works, but it's a wipe-and-restore if you're already on a major beta, and it's my main machine.
- Build on release macOS in CI instead — Xcode Cloud or a GitHub Actions macOS runner both run GA OS images, so the
BuildMachineOSBuildstamp is automatically clean. This also gets the headless-signing problem for free, because the runner's build user is the session user. - Wait for the OS to GA — correct but slow.
The CI path wins: it fixes the real bug (build-machine OS) and the detour bug (headless signing) in one move, and it means "works when I click Archive on my beta laptop" stops being load-bearing.
The takeaways
- App Store's
ITMS-9xxxxcodes are a lossy hash of "something about your binary is wrong." ITMS-90111 covers both an outdated Xcode and a beta build-machine OS. When the code contradicts what you can measure, believe your measurements and keep reading the binary's other stamps —BuildMachineOSBuildis the one everyone forgets. - Never opt your build machine into a macOS beta if it ships App Store builds. The Xcode being release doesn't save you; the OS stamp poisons the binary.
errSecInternalComponentduringcodesignreally does mean session context, not a locked keychain — reach forlaunchctl asuserbeforesecurity unlock-keychain. It's a genuine fix. It just wasn't this rejection's fix, and I nearly convinced myself it was because it made a real error go away at the same time.- A fix that resolves a real error is not proof it resolved the error. If the user-visible symptom is identical after your change, you changed the wrong variable.