xcodebuild Exit Code 65: What it is and how to solve for iOS and macOS builds
Xcodebuild exit code 65: for iOS and macOS developers with any CI system experience, these are frightening words. Some of you might not even be aware because Xcode hides those error codes from you.
In order to prevent xcodebuild
from just returning that exit code without any kind of error message, you have to understand what exit code 65
actually refers to.
If you type man xcodebuild
into your terminal emulator of choice and scroll to (almost) the bottom, you’ll see this little paragraph just before the usage examples:
This little hint leads to the sysexits
man page. Like HTTP status codes 200 OK, 404 Not Found or 418 I’m a teapot, common executable return codes are defined in the sysexits.h
header file. A common exit code is exit code 65
or bad data input.
Why does xcodebuild
exit with code 65?
Let’s examine why xcodebuild
seems to love exiting with code 65.
xcodebuild
is a complicated piece of software. It determines your project or workspace structure and will hand everything to a compiler along with the correct flags. A single command in the logs could range in length from 50 characters to filling an entire Safari window running in fullscreen mode on a 27” iMac.
If something doesn’t go the way xcodebuild
expected (which happens a lot), it will return exit code 65. What’s most frustrating is that xcodebuild
rarely leaves any context for the issue it couldn’t process. We grappled with one of these situations for almost a year.
Whenever xcodebuild
finishes executing one of its actions (clean, build, test, analyze), the next one is immediately executed without any preparation. At that point, fixed timeout counters are started. If your hardware isn’t fast enough, those timeouts hit 0 before the iOS Simulator has the chance to fully boot.
This problem was exacerbated in some setups because the iOS Simulator would boot on a background thread. This background thread would be de-prioritized if there was anything more important happening in another thread. Once the timeout hit 0, xcodebuild
would try and fail to connect to the iOS Simulator several times. This is because xcodebuild
sits between the compiled binary and the Simulator, so it doesn’t know the speed of the underlying hardware and can’t necessarily recover.
On your local machine, you could fix the issue by rolling your eyes, sighing, and hitting CMD+U
again. But on CircleCI, our system shut everything down and recycled the VM that your last build ran in.
Xcode in CircleCI workflows
In your normal development workflow, there is no derived data folder or other file common to Xcode. The working directory won’t exist, and no iOS Simulator will be running. The second a new build is kicked off on CircleCi, we’re faced with the same issue of xcodebuild
potentially being unable to recover from an unknown state and returning exit code 65.
The best way to mitigate this is to boot the iOS Simulator early with the help of the instrument’s command line tool. This will boot the iOS Simulator and immediately return, so xcodebuild
can start compiling your code. We usually recommend doing that separately from the dependency step to remind your future self/colleague that this is a vital build step — as essential as fetching dependencies through Carthage or updating fastlane.
dependencies:
pre:
- xcrun instruments -w 'iPhone 7 (10.3)' || sleep 15
Giving the iOS Simulator time to fully boot
That sleep 15
is important for two reasons:
- It gives the iOS Simulator some breathing room to fully boot and
- It returns a 0 exit code.
When chaining commands together without the use of set -o pipefail
, the last command’s exit code will be returned, and the earlier commands’ codes will be disregarded. CircleCI looks for the build step’s exit code to determine success or failure. Excluding the sleep command would mean that xcrun instruments
would return a non-0 exit code since the -t
(template) flag was not passed.
Conclusion
Hopefully, this explanation into the infamous xcodebuild
exit code 65 gives you some insight into why xcodebuild
does the things it does. Whenever you see that code, xcodebuild
has encountered a non-recoverable issue. Be assured that we’re continuing to work closely with Apple’s Developer Tools Team to make the developer toolchain faster and more stable.