This is the manual-signing version of a missing profile. Xcode was told which team and which bundle ID to sign for, it looked through the .mobileprovision files installed on this machine, and none of them covered that combination. It has no authority to go and create one, because manual signing means you supply the profiles.
Read the quoted identifier before anything else. It is formatted as team ID slash bundle ID, and the bundle ID is frequently not your app. Widgets, notification service extensions, watch apps, share extensions, and app clips each have their own bundle ID and each need their own profile. A build that signs the main app perfectly will still stop here on an extension nobody thought about.
Check what is actually installed
Profiles have a meaningless UUID filename, so listing the directory tells you nothing. Decode each one and print the identifier it covers.
PROFILES=~/Library/Developer/Xcode/UserData/Provisioning\ Profiles for f in "$PROFILES"/*.mobileprovision; do security cms -D -i "$f" > /tmp/p.plist 2>/dev/null || continue NAME=$(/usr/libexec/PlistBuddy -c 'Print :Name' /tmp/p.plist) APPID=$(/usr/libexec/PlistBuddy -c 'Print :Entitlements:application-identifier' /tmp/p.plist) EXP=$(/usr/libexec/PlistBuddy -c 'Print :ExpirationDate' /tmp/p.plist) echo "$APPID | $NAME | expires $EXP" done
The usual causes
- The profile was never installed on the runner. Nothing downloads profiles for you under manual signing.
- An extension target has no profile. The message names the extension's bundle ID, not the app's.
- The bundle ID in build settings does not match the profile, often because of a suffix like .debug or .staging applied by a build configuration.
- The team ID in the message is not the team that owns the profile you installed.
- The profile expired. An expired profile installs fine and is then ignored, which looks identical to it not being there.
- The profile covers a wildcard App ID, but your entitlements need a specific one. Wildcards cannot carry most capabilities.
The bundle ID suffix case deserves a note of its own, because it is easy to miss. A project that appends .debug or .staging per configuration produces a different bundle ID for each one, and each of those needs its own registered App ID and its own profile. A pipeline that ships Release fine and fails on a staging build is usually this, not a broken runner. Check PRODUCT_BUNDLE_IDENTIFIER for the configuration you are actually building, rather than the one you see in the project navigator.
Fix it on CI
- 1
Install every profile the build needs, not just the app's
Copy each .mobileprovision into the profiles directory before the build step. One per target that gets signed: app, extensions, watch app, app clip.
- 2
Name the profiles explicitly
Set PROVISIONING_PROFILE_SPECIFIER per target, or list each target's profile in ExportOptions.plist under provisioningProfiles. Leaving xcodebuild to guess is how the wrong profile gets picked on a runner with several installed.
- 3
Verify before building
Run the decode loop above as a pipeline step. Failing fast with a clear message beats a signing error four minutes into a compile.
PROFILES=~/Library/Developer/Xcode/UserData/Provisioning\ Profiles mkdir -p "$PROFILES" cp signing/*.mobileprovision "$PROFILES/"
In your ExportOptions.plist, every signed bundle ID needs an entry. This is the file that catches the extension problem, because an incomplete map here fails the export even when the archive succeeded.
<key>signingStyle</key> <string>manual</string> <key>teamID</key> <string>ABCDE12345</string> <key>provisioningProfiles</key> <dict> <key>com.example.app</key> <string>MyApp App Store</string> <key>com.example.app.NotificationService</key> <string>MyApp Notification Service App Store</string> </dict>
Or let xcodebuild fetch them
If you would rather not manage profile files at all, automatic signing works on CI as long as you authenticate with an App Store Connect API key instead of an Apple ID. The trade-off is that the build can now mint profiles in your team's portal, which some teams do not want happening from a runner.
xcodebuild archive \ -scheme MyApp \ -archivePath build/MyApp.xcarchive \ -allowProvisioningUpdates \ -authenticationKeyPath "$ASC_KEY_PATH" \ -authenticationKeyID "$ASC_KEY_ID" \ -authenticationKeyIssuerID "$ASC_ISSUER_ID"
With fastlane match the same error usually means the repo has no profile for that bundle ID yet, and the CI job runs with --readonly so it cannot create one. Run match once read-write from a machine with portal permissions to add the missing target, commit the result, and the readonly runs start working.
# On a developer machine, once, per missing target fastlane match appstore --app_identifier com.example.app.NotificationService # CI keeps using readonly fastlane match appstore --readonly