Skip to content

Instantly share code, notes, and snippets.

@apotema
Created April 9, 2026 21:01
Show Gist options
  • Select an option

  • Save apotema/dda042b82616bdfdb8f1583cc1e688f5 to your computer and use it in GitHub Desktop.

Select an option

Save apotema/dda042b82616bdfdb8f1583cc1e688f5 to your computer and use it in GitHub Desktop.
Zig 0.15.2 + macOS 26 Tahoe: DEVELOPER_DIR fix for arm64 linker errors

Zig 0.15.2 + macOS 26 (Tahoe): DEVELOPER_DIR fix for arm64 linker errors

TL;DR

export DEVELOPER_DIR=/Library/Developer/CommandLineTools

Add that to ~/.zshrc (or ~/.bashrc). Every zig build, zig build run, zig build test, and any tool that wraps them (e.g. labelle) will start working again.

Symptoms

On macOS 26 (Tahoe) with Zig 0.15.2 (aarch64), a fresh zig init project fails to build with a wall of undefined-symbol errors when linking the build runner itself:

error: undefined symbol: __availability_version_check
error: undefined symbol: _abort
error: undefined symbol: _arc4random_buf
error: undefined symbol: _bzero
error: undefined symbol: _clock_gettime
error: undefined symbol: _dispatch_queue_create
error: undefined symbol: _exit
error: undefined symbol: _fork
error: undefined symbol: _free
error: undefined symbol: _getcwd
error: undefined symbol: _getenv
error: undefined symbol: _isatty
error: undefined symbol: _malloc_size
error: undefined symbol: _posix_memalign
error: undefined symbol: _sigaction
error: undefined symbol: _waitpid
...

Why it happens

Zig auto-detects the macOS SDK via xcrun --show-sdk-path. On Tahoe, xcrun returns the full Xcode SDK:

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.4.sdk

That SDK's usr/lib/libSystem.tbd declares its targets as:

targets: [ x86_64-macos, x86_64-maccatalyst, arm64e-macos, arm64e-maccatalyst ]

No arm64-macos — only arm64e-macos. Zig 0.15.2 links arm64-macos by default, so the linker loads libSystem, finds no symbols under its target, and dumps ~25 undefined-symbol errors. This affects the build.zig runner itself, so the failure happens before any project code compiles.

The Command Line Tools SDK at /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk (currently MacOSX26.2 via symlink) still ships arm64-macos in its libSystem targets and links cleanly. DEVELOPER_DIR is the env var that tells xcrun which developer installation to use; pointing it at the CLI tools makes Zig pick the working SDK.

The fix

Per-command:

DEVELOPER_DIR=/Library/Developer/CommandLineTools zig build

Current shell:

export DEVELOPER_DIR=/Library/Developer/CommandLineTools
zig build

Persistent (zsh):

echo 'export DEVELOPER_DIR=/Library/Developer/CommandLineTools' >> ~/.zshrc

Persistent (bash):

echo 'export DEVELOPER_DIR=/Library/Developer/CommandLineTools' >> ~/.bashrc

Verify the fix is active

ZIG_VERBOSE_LINK=1 zig build 2>&1 | grep syslibroot

Working (note the CLI tools path):

zig ld ... -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk ...

Broken (Xcode path):

zig ld ... -syslibroot /Applications/Xcode.app/.../MacOSX26.4.sdk ...

Things that don't work

  • SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX15.4.sdk zig build — Zig ignores SDKROOT; it goes through xcrun.
  • -Dtarget=aarch64-macos — changes the output target, not the SDK used to build the build runner itself.
  • Replacing Zig's bundled lib/libc/darwin/libSystem.tbd with a symlink to the SDK's file — Zig's self-hosted Mach-O linker uses -syslibroot from xcrun, not the bundled stub.
  • xcode-select --switch /Library/Developer/CommandLineTools — works in principle but is more invasive than DEVELOPER_DIR (changes everything on the machine, not just your shell), and you'll forget you did it.

If you still see the error after exporting

Nuke the Zig cache once to clear any stale pre-linked artifacts:

rm -rf ~/.cache/zig

When is this no longer needed?

When you upgrade Zig past 0.15.2 to a version that detects Tahoe's SDK layout and prefers the CLI tools SDK automatically. Check zig version. If you're on a version newer than 0.15.2 and this workaround is no longer documented as needed in the Zig release notes, remove the export.

Tracking:

  • ziglang/zig#25928 — Support TBD v5 in Mach-O linker (related upstream work)
  • ziglang/zig#15963 — the macOS 14 Sonoma tracking issue, which has the same pattern of workarounds

What also benefits from this fix

Anything that spawns zig build as a child process, as long as it doesn't clear the environment:

  • Raw zig build / zig build run / zig build test
  • Build-tool wrappers like labelle (verified: source uses std.process.Child.run without touching env_map, so env vars inherit)
  • IDE integrations that inherit your shell environment

Will not work for GUI-launched apps that don't inherit shell env vars (e.g. VS Code opened from Finder instead of the terminal). For those, use launchctl setenv DEVELOPER_DIR /Library/Developer/CommandLineTools in a login hook, or just launch the IDE from the terminal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment