An infamous „Command PhaseScriptExecution failed with a nonzero exit code” haunts mostly React Native, Flutter and Cordova developers. The biggest issue with this is that Xcode won’t tell you which exact script fails. When searching through the web you can find two common solutions. Or I should rather say workarounds.
Workarounds
First one suggest recreating pods:
- Close Xcode/ Terminals / Restart MacOS
- Remove Podfile.lock
- pod update && pod install or pod deintegrate && pod install
- Open Xcode, Clean Project, Build
The second suggested option focuses on Keychain Access:
- Right click login
- Lock
- Unlock
- Open Xcode, Clean Project, Build
While either may work at some situations, it’s much better to find a root cause of the issue. In most cases that means you have added a script that fails. Probably invalid credentials, invalid hash, maybe a path or some 3rd party library. Using the above methods you could just wonder which one would that be.
How to find out which script is failing?
There are two options. You can go to Xcode -> Target -> Build Phases. There you will find all list of scripts that run through the build. You can go through them and guess or,
open terminal, navigate to your project folder and run a build:
xcodebuild -workspace App.xcworkspace -scheme App -configuration Debug -destination generic/platform=iOS -archivePath App.xcarchive archive CONFIGURATION_BUILD_DIR=/PATH_TO_YOUR_PROJECT/ios/build/device SHARED_PRECOMPS_DIR=/PATH_TO_YOUR_PROJECT/ios/build/sharedpch
Make sure to type correct paths, workspace, schemes and configurations. You can find them using commands like:
xcodebuild -list -project App.xcodeproj
xcodebuild -list -workspace App.xcworkspace
or you can find them in Xcode.
You can also tail the build to last few lines:
xcodebuild -workspace App.xcworkspace -scheme App -configuration Debug -destination generic/platform=iOS -archivePath App.xcarchive archive CONFIGURATION_BUILD_DIR=/PATH_TO_YOUR_PROJECT/ios/build/device SHARED_PRECOMPS_DIR=/PATH_TO_YOUR_PROJECT/ios/build/sharedpch | tail -n 5
At the end of the output you should find which script is failing. In this example I intentionally put wrong path to sentry.properties and got something like this:
The following build commands failed:
PhaseScriptExecution Upload\ Debug\ Symbols\ to\ Sentry /Users/*redacted*/Library/Developer/Xcode/DerivedData/App-abcluaxbgwcrvgallomdzotrtlty/Build/Intermediates.noindex/ArchiveIntermediates/App/IntermediateBuildFilesPath/App.build/Debug-iphoneos/App.build/Script-FE82BF8431AC42B3A03A7EF1.sh (in target 'App' from project 'App')
(1 failure)
Clearly there’s some script called „Script-FE82BF8431AC42B3A03A7EF1.sh” that breaks. It can be viewed or executed it. Let’s execute it and see the results:
/Users/*redacted*/Library/Developer/Xcode/DerivedData/App-abcluaxbgwcrvgallomdzotrtlty/Build/Intermediates.noindex/ArchiveIntermediates/App/IntermediateBuildFilesPath/App.build/Debug-iphoneos/App.build/Script-FE82BF8431AC42B3A03A7EF1.sh
/Users/*redacted*/Library/Developer/Xcode/DerivedData/App-abcluaxbgwcrvgallomdzotrtlty/Build/Intermediates.noindex/ArchiveIntermediates/App/IntermediateBuildFilesPath/App.build/Debug-iphoneos/App.build/Script-FE82BF8431AC42B3A03A7EF1.sh: line 20: ../../BREAK_BUILD/node_modules/@sentry/cli/bin/sentry-cli: No such file or directory
If the path contains BREAK_BUILD is must not right. I see which library is failing, I can narrow down the script and find exactly what caused the problem without reconfiguring the app & platforms. DO NOT edit the script in Build folder as it will be overridden. Find it in your project and there do the fix. If you can’t file the script file, it may be added in your poject.pbxproj which was my case:
FE82BF8431AC42B3A03A7EF1 /* Upload Debug Symbols to Sentry */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Upload Debug Symbols to Sentry";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "echo \"warning: uploading debug symbols - set SENTRY_SKIP_DSYM_UPLOAD=true to skip this\"\nif [ -n \"$SENTRY_SKIP_DSYM_UPLOAD\" ]; then\n echo \"warning: skipping debug symbol upload\"\n exit 0\nfi\nexport SENTRY_PROPERTIES=/Users/*redacted*/sentry.properties\nfunction getProperty {\n PROP_KEY=$1\n PROP_VALUE=`cat $SENTRY_PROPERTIES | grep \"$PROP_KEY\" | cut -d'=' -f2`\n echo $PROP_VALUE\n}\nif [ ! -f $SENTRY_PROPERTIES ]; then\n echo \"warning: SENTRY: sentry.properties file not found! Skipping symbol upload.\"\n exit 0\nfi\necho \"# Reading property from $SENTRY_PROPERTIES\"\nSENTRY_CLI=$(getProperty \"cli.executable\")\nSENTRY_COMMAND=\"../../$SENTRY_CLI upload-dsym\"\n$SENTRY_COMMAND";
};
The above you should also find in Xcode Build Phases, this time you know which one exactly fails.
After study the script, I can see that in sentry.properties I made a mistake:
defaults.url=https://sentry.io/
defaults.org=app
defaults.project=app
auth.token=*redacted*
cli.executable=BREAK_BUILD/node_modules/@sentry/cli/bin/sentry-cli
Easy! I hope next time you see „Command PhaseScriptExecution failed with a nonzero exit code” it won’t scare you so much anymore.