I upgraded my MacBook Air to macOS 27 Golden Gate this week. The next time I needed the AWS CLI, I got this:
zsh: bad CPU type in executable: aws It had worked the day before. Same laptop, same aws that had been sitting in /usr/local/bin since February 2025.
The CPU hadn't changed overnight. So what had?
What the error is telling you
"Bad CPU type in executable" isn't zsh being dramatic. It's errno 86, EBADARCH, straight from the kernel. The exec call looked at the binary, found no code for this machine's architecture, and refused. zsh just printed the message.
Two commands settle it:
$ uname -m
arm64
$ file "$(readlink -f "$(which aws)")"
/usr/local/aws-cli/aws: Mach-O 64-bit executable x86_64 An Apple silicon Mac and an Intel-only binary. No arm64 slice, not universal.
Which means this aws had never run natively on this laptop. For a year and a half, every aws s3 ls went through Rosetta 2, and I never noticed. That's the whole point of Rosetta, and also why this failure comes as such a surprise.
Rosetta was gone
You can test Rosetta without involving the broken tool at all. /usr/bin/true is a universal binary, so ask for its Intel half:
$ arch -x86_64 /usr/bin/true
arch: posix_spawnp: /usr/bin/true: Bad CPU type in executable Same error, from a binary Apple ships. That rules out the AWS CLI. The machine can't run Intel code at all.
The runtime lives under /Library/Apple, and after the upgrade mine looked like this:
$ ls /Library/Apple/usr/libexec/oah/
RosettaLinux Only the piece used for Linux virtual machines was left. The part that translates macOS binaries was missing, and the oahd daemon wasn't running.
My install log says Rosetta was alive and being updated as recently as 20 August:
2026-08-20 01:05:35+02 system_installd[844]: Installed "RosettaUpdateAuto" () Then the macOS 27 upgrade ran on the night of 15 September, and the first Rosetta line after it reads like a small obituary:
2026-09-16 00:02:32+02 mbfloagent[1226]: Rosetta flplugin: daemon port null! It isn't a bug, it's a release note
I assumed the upgrade had gone wrong. It hadn't. Apple's macOS 27 release notes list this under Rosetta, in the Deprecations section:
If Rosetta was previously installed, it is not automatically restored after upgrading to macOS 27.0. (163213094)
One sentence, filed where almost nobody who just wants to run aws will ever look.
Rosetta itself is still supported. Apple's support page says it's available on macOS 27 or earlier. It just doesn't survive the upgrade, and nothing tells you. A GUI app gets a friendly prompt offering to install it. A binary launched from a shell gets errno 86.
The same release notes say where this is heading: "All Intel-based software will no longer be compatible with macOS 28.0, excluding legacy games."
Fix 1: stop needing Rosetta
My CLI was 2.24.11, installed from the official .pkg. Back then that package was Intel-only, on every Mac. AWS started shipping universal macOS installers with 2.30.0 in September 2025. If yours predates that and you never upgraded, you've been running under Rosetta too.
I removed the old install and let Homebrew provide a native one:
sudo rm -rf /usr/local/aws-cli /usr/local/bin/aws /usr/local/bin/aws_completer
brew install awscli $ aws --version
aws-cli/2.36.47 Python/3.14.7 Darwin/27.0.0 source/arm64 If you'd rather stay on the official installer, download the current AWSCLIV2.pkg and run it over the top. Either way ~/.aws isn't touched, so profiles, SSO sessions and credentials carry over.
One thing to check after a Homebrew install: if the old copy is still in /usr/local/bin and that directory comes first in your PATH, you'll keep hitting the dead binary. which -a aws shows every copy in order.
Fix 2: put Rosetta back
If the broken tool has no native build, reinstall the translator:
softwareupdate --install-rosetta --agree-to-license On Golden Gate the install prompt comes with a warning that Intel apps won't open on macOS 28. Take it literally. This buys you about a year.
I didn't run it. I had one Intel-only tool on my PATH and a native replacement for it, so bringing back a translation layer with a published end date made no sense.
Find the rest before they find you
The AWS CLI was just the first thing I happened to type. Anything else compiled for Intel only is broken in the same way and will stay quiet until the day you need it.
This lists Intel-only executables in the usual places:
find /usr/local/bin /opt/homebrew/bin ~/.local/bin -maxdepth 1 2>/dev/null | while read -r f; do
t=$(file -bL "$f")
[[ $t == *x86_64* && $t != *arm64* ]] && echo "$f"
done Mine came back empty once the old CLI was gone. If yours doesn't, the usual suspects are vendor .pkg installers from a few years ago, single-binary tools downloaded from GitHub releases when only the amd64 asset existed, and anything under /usr/local left over from an Intel Homebrew that was migrated from an older Mac.
macOS 27 also lists Intel-based apps under Settings > General. That list is built around apps. I wouldn't trust it to know about a lone binary in /usr/local/bin.
The same errno, wearing a Node costume
That clean scan felt good for about ten minutes. Then I ran this blog's production build to check the post you're reading, and the build died:
DONE Compiled successfully
Error: spawn Unknown system error -86
at ChildProcess.spawn (node:internal/child_process:458:11)
...
at NotificationCenter.notifyRaw (node_modules/node-notifier/notifiers/notificationcenter.js:81:11)
at WebpackNotifierPlugin.compilationDone (node_modules/webpack-notifier/index.js:129:14) Minus 86. Node doesn't have a name for EBADARCH, so you get "Unknown system error" and the raw number. It's the same refusal from the same kernel.
This site still builds its CSS with Laravel Mix 5, which pops a desktop notification when webpack finishes. It does that through node-notifier 9.0.1, which carries its own copy of terminal-notifier inside node_modules:
$ file node_modules/node-notifier/vendor/mac.noindex/terminal-notifier.app/Contents/MacOS/terminal-notifier
.../terminal-notifier: Mach-O 64-bit executable x86_64 The compile had succeeded. The "it worked" toast is what failed, with a non-zero exit, so the Eleventy step after && never ran. One line in webpack.mix.js fixed it:
mix.disableNotifications() So npm install can plant Intel-only binaries in any project on your disk, and the scan above will never look there. If a Node toolchain starts throwing spawn Unknown system error -86 after the upgrade, read the stack trace for whatever it tried to execute and run file on that.
Same error, other tools
Since this post went up, people have been landing on it with the same error from tools that aren't the AWS CLI. The fix is the same idea every time: find the Intel binary and swap it for a native one. What changes is where the Intel binary came from.
The wording changes too, depending on who's complaining:
zsh: bad CPU type in executable: kubectl
-bash: /usr/local/bin/git: Bad CPU type in executable
arch: posix_spawnp: /usr/bin/true: Bad CPU type in executable
Error: spawn Unknown system error -86
OSError: [Errno 86] Bad CPU type in executable Some tools skip the words and print exec failed, error: 86. It's all errno 86.
git, Homebrew, and anything else in /usr/local/bin
Homebrew on Apple silicon lives in /opt/homebrew. /usr/local is where Intel Homebrew lived. If your git resolves to /usr/local/bin/git, it's probably left over from an Intel Homebrew, often one that Migration Assistant carried over from an older Mac along with every other formula. file -L /usr/local/bin/git settles it. Don't count on the old brew next to it for the cleanup either, since it fails the same way if its bundled Ruby is Intel-only. If brew itself is what throws the error, which brew probably says /usr/local/bin/brew, and you're in exactly this situation.
Its Cellar still tells you what it had installed. Reinstall those with the native Homebrew (install it from brew.sh first if /opt/homebrew doesn't exist):
ls /usr/local/Cellar
/opt/homebrew/bin/brew install git
which -a git /opt/homebrew/bin/git should now come first. If it doesn't, add eval "$(/opt/homebrew/bin/brew shellenv)" to ~/.zprofile so the native prefix wins over /usr/local/bin, then run the same line in your current shell or open a new one.
node
Official prebuilt Node binaries for Apple silicon start at Node 16. For anything older, the usual way onto an M-series Mac was to install it from a Rosetta shell, which is the workaround nvm's README recommends. That gives you an x86_64 node, and after the upgrade it's dead:
file -L "$(which node)" Move the project to a current LTS with nvm install --lts, from a normal shell rather than the old Rosetta one, and check that node -p process.arch prints arm64. If you keep an Intel build of Node 14 around for an old .nvmrc, it needs Rosetta today and won't run at all on macOS 28.
If node itself is fine and something it spawns fails with -86, that's the node_modules case from the previous section.
kubectl, and other single-binary downloads
kubectl's install docs make you pick an architecture in the download URL. Copy the darwin/amd64 line once and you have an Intel kubectl until you replace it. Replace it with the arm64 one:
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/arm64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/kubectl Or let Homebrew handle it with brew install kubectl. The same goes for anything you grabbed from a GitHub releases page back when the amd64 asset was the only Mac option. Download the darwin_arm64 asset, and check it with file before you trust it.
session-manager-plugin
If you use aws ssm start-session, a native AWS CLI isn't the end of it. The Session Manager plugin is a separate install, and AWS ships separate macOS bundles for Intel and Apple silicon. The old Intel one sits in /usr/local/sessionmanagerplugin and dies with the same errno the moment the CLI tries to hand the session over.
Replace it with the mac_arm64 bundle:
curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac_arm64/sessionmanager-bundle.zip" -o sessionmanager-bundle.zip
unzip sessionmanager-bundle.zip
sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin Or brew install --cask session-manager-plugin. Either way, session-manager-plugin --version should answer instead of erroring.
firebase
The Firebase CLI's standalone installer (curl -sL https://firebase.tools | bash) only asks which OS you're on, not which CPU. On a Mac it downloads a single binary, and as of this week that binary is still Intel-only:
$ file firebase-macos
firebase-macos: Mach-O 64-bit executable x86_64 So reinstalling the standalone build won't help. Remove it and use the npm package, which runs on whatever Node you have and is native as long as your Node is:
sudo rm /usr/local/bin/firebase
npm install -g firebase-tools claude
Claude Code's native installer puts its binary in ~/.local/bin, which the scan above covers. If file -L "$(which claude)" says x86_64, run the current installer again from its docs. It now checks whether the shell is running under Rosetta and downloads the arm64 build either way.
Takeaway
Nothing was wrong with the AWS CLI. It was an Intel binary that had been quietly translated for eighteen months, and the upgrade removed the translator without saying so.
When something that "used to work fine" dies with bad CPU type in executable right after a macOS upgrade, run file on it before you reinstall anything. If it says x86_64 and nothing else, you've found a tool that was living on borrowed time, and macOS 28 is when the loan gets called in for good.
Replace it now, while it's one binary and not a Monday morning.
Did an OS upgrade break a tool that had worked for years? Send me the error and I'll take a look.
