Skip to content

BUG: Clang linker fails on macOS 27 Beta ARM64 — ld: library 'd64' not found #2268

Description

@Bllq21

Describe the issue:

Related issues


Environment

OS macOS 27.0 Developer Beta (arm64)
Chip Apple M3 Pro (11 cores: 5P + 6E), 36GB RAM
Python 3.12.13 (via mise)
PyMC 6.0.1
PyTensor 3.0.7
NumPy 2.4.6
Clang /usr/bin/clang++ (clang-2100.3.23.3)

Root cause (as understood)

-ld64 in pytensor/link/c/cmodule.py is meant to select the classic macOS
linker (ld64) over Apple's new linker (ld/lld). On macOS 27 Beta, Clang
parses this as -l d64 (link against library d64) rather than as a linker
selection flag, because the flag syntax changed in the Xcode toolchain bundled
with this beta OS version.


Workaround (confirmed working)

Setting pytensor.config.cxx = "" before importing PyMC forces PyTensor to
use its Python-mode backend (no C compilation), bypassing the Clang flag entirely.

import pytensor
pytensor.config.cxx = ""  # must be set before `import pymc`
 
import pymc as pm
# ... rest of code works normally

Performance impact on Apple Silicon (M3 Pro, Python-mode vs C-compiled):

  • Observed: ~920 draws/s in Python mode
  • This was sufficient for practical use (1600 draws in ~2 seconds)
  • Python mode may be significantly slower on less capable hardware
    Alternatively, via environment variable before running the script:
export PYTENSOR_FLAGS="cxx="
python your_script.py

Reproducable code example:

python
import pymc as pm
import numpy as np
 
with pm.Model():
    mu = pm.Normal("mu", mu=0, sigma=1)
    pm.sample(100, tune=100, chains=2)

Error message:

pytensor.link.c.exceptions.CompileError: Compilation failed (return status=1):
clang++ -dynamiclib -g -Wno-c++11-narrowing -fno-exceptions -fno-unwind-tables
-fno-asynchronous-unwind-tables -DNPY_NO_DEPRECATED_API=NPY_1_7_API_VERSION
-fPIC -undefined dynamic_lookup -ld64
-I/.../numpy/_core/include
-I/.../python3.12/include/python3.12
-I/.../pytensor/link/c/c_code
-L/.../python3.12/lib
-fvisibility=hidden
-o ...lazylinker_ext.so ...mod.cpp
ld: library 'd64' not found
clang++: error: linker command failed with exit code 1

PyTensor version information:

Name: pytensor
Version: 3.0.7

Apple clang version 21.0.0 (clang-2100.3.23.3)
Target: arm64-apple-darwin27.0.0
Thread model: posix

Context for the issue:

This issue appears to affect all recent macOS versions (15.3+, 15.4, and now 27
Beta), with slightly different error messages depending on exact Xcode/Clang
version. The common root is the -ld64 flag in PyTensor's compile command being
incompatible with Apple's evolving linker toolchain. A more robust fix would be
to detect the macOS linker version at compile time and either omit -ld64 or
replace it with -Wl,-ld_classic on macOS versions where the latter is supported.

Activity

  1. SneachChea commented on Sep 14, 2026

    @SneachChea

    Confirming this on macOS 27.0 (26A428) + Command Line Tools 27.0.0.0 + Apple clang 21.0.0 (clang-2100.3.34.2), PyTensor 3.2.3 / PyMC 6.2.0.

    The flag is still on main (pytensor/link/c/cmodule.py, gated on int(platform.mac_ver()[0].split(".")[0]) >= 15), so no released version is unaffected.

    Two things that may narrow the fix:

    1. On this toolchain the classic-linker selection is not renamed, it is gone:

      • clang++ -ld64 → ld: library 'd64' not found
      • clang++ -Wl,-ld64 → same error, so ld itself rejects it too
      • clang++ -Wl,-ld_classic → ld: warning: -ld_classic is no longer supported and will be ignored

      The mac_ver() >= 15 gate therefore has no target left: on macOS ≥ 27 the flag should just be dropped.

    2. cxx="" works but disables C compilation entirely — much slower, and it rules out samplers that need the compiled logp. A wrapper that strips the flag keeps C compilation:

      cat > /usr/local/bin/clang-no-ld64 <<'EOF'
      #!/bin/bash
      args=()
      for a in "$@"; do [[ "$a" == "-ld64" ]] || args+=("$a"); done
      exec /usr/bin/clang++ "${args[@]}"
      EOF
      chmod +x /usr/local/bin/clang-no-ld64
      export PYTENSOR_FLAGS="cxx=/usr/local/bin/clang-no-ld64"   # or cxx = ... in .pytensorrc

      Verified: models that failed with the exact CompileError above compile and sample normally.

    3. Why it looks like two errors: .so files compiled before the OS upgrade keep loading, so the failure only appears on the next recompile. Any Python patch bump is enough, since the compiledir tag changes (compiledir_macOS-27.0-arm64-arm-64bit-arm-3.12.14-64) and the lazylinker must be rebuilt — hence the follow-up "Version check of the existing lazylinker compiled file. Looking for version 0.32, but found None".

  2. drbenvincent commented on Sep 19, 2026

    @drbenvincent
    Contributor

    Can I request your attention on this one, maybe @ricardoV94? This is a major issue for anyone who has updated to MacOS 27

  3. ricardoV94 commented on Sep 20, 2026

    @ricardoV94
    Member

    Can I request your attention on this one, maybe @ricardoV94? This is a major issue for anyone who has updated to MacOS 27

    Are you installing via conda?

  4. SneachChea commented on Sep 20, 2026

    @SneachChea

    In my case, no conda involved. I’m installing from PyPI using uv in a virtual environment, with Homebrew Python 3.12.14 (macOS ARM64), PyTensor 3.2.3 and PyMC 6.2.0.

  5. ricardoV94 commented on Sep 20, 2026

    @ricardoV94
    Member

    Can you try with conda. The suggested way of working with C backend and respective compilers is to use the conda release/env. If that fails equally we need to take a look

  6. drbenvincent commented on Sep 21, 2026

    @drbenvincent
    Contributor

    I have applied and validated a host-local workaround on macOS 27.0 / Apple Clang 21. It unblocks current development across six separate PyTensor worktrees while retaining C compilation: a forced PyTensor C-link and a small PyMC NUTS sample both pass.

    Workaround applied on this machine

    ~/.local/bin/pytensor-clang++-macos27 (made executable):

    #!/bin/bash
    set -euo pipefail
    
    if [[ "$(/usr/bin/uname -s)" == "Darwin" ]]; then
        macos_major="$(/usr/bin/sw_vers -productVersion | /usr/bin/cut -d. -f1)"
    else
        macos_major=0
    fi
    
    if [[ "$macos_major" =~ ^[0-9]+$ ]] && ((macos_major >= 27)); then
        args=()
        for arg in "$@"; do
            [[ "$arg" == "-ld64" ]] || args+=("$arg")
        done
        exec /usr/bin/clang++ "${args[@]}"
    fi
    
    exec /usr/bin/clang++ "$@"

    ~/.pytensorrc:

    [global]
    cxx=/absolute/path/to/pytensor-clang++-macos27

    The shim removes only -ld64 on macOS >= 27 and passes every other compiler argument through. Because PyTensor reads this user configuration, it applies to all local PyTensor environments without changing repository files or individual virtualenvs. Existing Python kernels need a restart.

    This is a local mitigation, not a proposed substitute for the upstream fix. To answer the installation question cleanly, I will upgrade another machine to macOS 27 and test the same minimal reproduction in fresh pip and conda environments with this shim disabled, then report back.

  7. alex-andrzejek-og commented on Oct 7, 2026

    @alex-andrzejek-og

    Adding a data point: this is triggered by the Command Line Tools / Xcode 27.0 toolchain, not by macOS 27. It reproduces on macOS 26.7 (25G229) once Software Update installs Command Line Tools for Xcode 27.0, which it offers to the previous macOS as a separate update.

    • macOS 26.7 (25G229), arm64, no Xcode.app; xcode-select -p → /Library/Developer/CommandLineTools
    • Command Line Tools for Xcode 27.0 (pkgutil --pkg-info=com.apple.pkg.CLTools_Executables → 27.0.0.0.1788430756), Apple clang 21.0.0, ld-27037.1
    • PyTensor 2.38.3 / PyMC 5.28.5 from PyPI in a uv venv (no conda), Python 3.12.13
    • The same machine compiled fine on macOS 26.6.x with Command Line Tools 26.6. The first fresh compile after the 27.0 tools landed fails with ld: library 'd64' not found.

    So a fix or workaround gated on macOS >= 27 would miss this case. Both shims earlier in this thread gate that way, so they're no-ops on macOS 26 with the 27 tools. The flag should also not be dropped for every macOS ≥ 15: older toolchains may still rely on -ld64 to avoid the Xcode 15 ld_prime problems the gate was written for.

    Suggested fix: decide from the linker, not the OS version. Probe once per process: link a trivial shared object with -ld64, and drop the flag only if that fails. For example, in compile_args:

    def _ld64_supported(cxx):
        # tiny C file -> cxx -shared -undefined dynamic_lookup -ld64 t.c -o t.so
        # return False if it fails with "library 'd64' not found"

    We're carrying this as a local patch (wrapping GCC_compiler.compile_args). It has been verified with the PyMC NUTS and nutpie samplers, and it's a no-op on toolchains that still accept -ld64.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions