The archive job reports success, but the pipeline exits during -exportArchive. Failures like this are easily obscured by the final “export failed” message. The actual cause may be a mismatch between the export method and signing assets, changed entitlements within the archive, or CI invoking a different Xcode installation. Instead of repeatedly rerunning the pipeline, preserve the same xcarchive and split archiving, export, and diagnostics into independently reproducible steps.
Preserve the Failure Evidence First
Do not immediately clean the working directory or overwrite the original archive after a failure. At a minimum, preserve the xcarchive, the actual ExportOptions.plist used, the complete standard output, and the Xcode path and version. RunnerVM dedicated physical nodes are well suited to retaining this evidence, but the pipeline should still copy diagnostic files into the job artifact directory.
set -euo pipefail
RUN_DIR="$PWD/export-diagnostics"
ARCHIVE_PATH="$PWD/build/App.xcarchive"
mkdir -p "$RUN_DIR"
xcode-select -p > "$RUN_DIR/xcode-path.txt"
xcodebuild -version > "$RUN_DIR/xcode-version.txt"
sw_vers > "$RUN_DIR/macos-version.txt"
cp ExportOptions.plist "$RUN_DIR/ExportOptions.plist"
ditto "$ARCHIVE_PATH" "$RUN_DIR/App.xcarchive"
Record a checksum of the archive as well. If someone replaces the archive later, you can immediately tell that the object under investigation has changed.
ditto -c -k --keepParent "$ARCHIVE_PATH" "$RUN_DIR/App.xcarchive.zip"
shasum -a 256 "$RUN_DIR/App.xcarchive.zip" > "$RUN_DIR/checksums.txt"
An export failure does not mean the archive is invalid. First establish whether the archive is complete, then determine whether the problem belongs to the archive stage or the distribution stage.
Check Whether the Archive Is Exportable
An xcarchive is essentially a directory. First verify that its metadata, application bundle, and executable are present, then inspect the application identifier and version declared by the archive. Do not construct paths from the project name; retrieve them from Info.plist.
INFO="$ARCHIVE_PATH/Info.plist"
APP_RELATIVE=$(/usr/libexec/PlistBuddy -c \
"Print :ApplicationProperties:ApplicationPath" "$INFO")
APP_PATH="$ARCHIVE_PATH/Products/$APP_RELATIVE"
plutil -lint "$INFO"
test -d "$APP_PATH"
test -f "$APP_PATH/Info.plist"
plutil -p "$INFO"
plutil -p "$APP_PATH/Info.plist"
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
Pay particular attention to ApplicationPath, CFBundleIdentifier, the version number, and the signature verification result. If the application bundle is missing entirely or codesign --verify already fails, investigate the archive command, build-phase scripts, or artifact-copying process instead of continuing to adjust export options.
Compare Application Entitlements with the Provisioning Profile
The export stage reevaluates signing relationships. Export the application’s actual entitlements and the embedded provisioning profile separately, then compare their structures.
codesign -d --entitlements "$RUN_DIR/app-entitlements.plist" "$APP_PATH"
PROFILE="$APP_PATH/embedded.mobileprovision"
security cms -D -i "$PROFILE" > "$RUN_DIR/profile.plist"
plutil -extract Entitlements xml1 \
-o "$RUN_DIR/profile-entitlements.plist" \
"$RUN_DIR/profile.plist"
plutil -p "$RUN_DIR/app-entitlements.plist"
plutil -p "$RUN_DIR/profile-entitlements.plist"
Do not compare only the textual ordering of the files. Check whether the provisioning profile permits every entitlement used by the application and whether the application identifier, team identifier, and environment-specific entitlements match. Inspect every extension target as well, because the main application passing validation does not guarantee that all embedded components can be exported.
Validate ExportOptions.plist
Run plutil -lint ExportOptions.plist first to eliminate formatting errors, then consult the current Xcode help output to confirm which keys and values are available. Different versions may change the accepted method values or deprecate older syntax, so do not copy a configuration from a previous job and run it without verification.
A minimal configuration can include only the fields required by the current distribution workflow:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>development</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
</dict>
</plist>
The method shown here is only an example for a development export and must not be reused unchanged for other distribution targets. Run the following commands first and follow the documentation provided by the active toolchain:
plutil -lint ExportOptions.plist
xcodebuild -help > export-help.txt
grep -A 120 "Available keys for -exportOptionsPlist" export-help.txt
If you use manual signing, also verify that the configuration mappings cover the main application and every extension. Missing mappings, incorrect identifiers, or mixed signing styles often surface only during export.
Replay the Export Separately and Preserve the Full Log
Once the archive has been confirmed as valid, run the export separately using fixed absolute paths. Do not let the script delete temporary directories immediately after a failure.
set +e
xcodebuild -exportArchive \
-archivePath "$ARCHIVE_PATH" \
-exportPath "$RUN_DIR/exported" \
-exportOptionsPlist "$RUN_DIR/ExportOptions.plist" \
2>&1 | tee "$RUN_DIR/export.log"
STATUS=${PIPESTATUS[0]}
set -e
printf '%s
' "$STATUS" > "$RUN_DIR/export-status.txt"
exit "$STATUS"
In the log, look for the earliest specific error rather than the summary at the end. As an initial filter, search for signing, provisioning profiles, entitlements, and the export method:
grep -Ein \
"error:|provision|entitlement|certificate|signing|export" \
"$RUN_DIR/export.log" > "$RUN_DIR/export-errors.txt" || true
xcodebuild sometimes prints the path to a distribution log bundle. Archive that entire directory. It usually contains the decisions made at each stage of the IDEDistribution process and is more likely to reveal the root cause than the few error lines captured by the pipeline.
Create a Post-Fix Acceptance Checklist
After applying a fix, do not rely on the exit code alone. At a minimum, confirm that the export directory contains the expected file, signature verification passes, and the application identifier and version match the archive. Preserve the final ExportOptions.plist together with the Xcode version.
Add the following checks to the end of the job:
- The checksum of the compressed archive has not changed.
- The current
xcode-select -pmatches the expected toolchain. - The main application and all extensions pass signature verification.
- The application’s entitlements do not exceed those permitted by the provisioning profile.
- The export configuration passes
plutilvalidation, and its keys and values are supported by the current Xcode version. - The export log, distribution log bundle, and final artifacts are preserved together.
- The fix changes only one variable and can be verified repeatedly against the original archive.
With this approach, archive export is no longer an opaque, one-shot operation. The team can clearly distinguish toolchain drift, configuration errors, signing relationship problems, and archive corruption, while validating most fixes without rebuilding.
Frequently asked questions
Should I rebuild the archive as soon as export fails?
No. Preserve the original xcarchive and inspect its metadata, embedded profile, application bundle, and entitlements first. Rebuilding can erase the evidence needed to identify an export-only failure.
Can one ExportOptions.plist be reused across Xcode versions?
Not safely without validation. Check the xcodebuild help output from the selected Xcode version, verify supported method values, and validate the property list before every export.
Run your next build on a dedicated physical Mac
Choose a cloud Mac node for each task duration, then manage orders, connection details, and support tickets from the console.