From 55343e89fb8297bdd0d8a98ed04c383037a6531d Mon Sep 17 00:00:00 2001 From: "Ruben G." <161253222+Rub3n-0lte4n@users.noreply.github.com> Date: Sun, 7 Jun 2026 19:28:37 +0300 Subject: [PATCH] fix(setup): clear error when setup runs under x86/Rosetta Python (#941) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a check_arch() guard that fails fast with actionable guidance when setup runs on Apple Silicon under an Intel (x86_64) Python via Rosetta — otherwise compiled deps (bcrypt, pydantic-core, …) load as the wrong architecture and crash later with a cryptic "incompatible architecture" import error. Also catch that specific error around the bcrypt import and print rebuild steps. Rebased onto current main: the start-macos.sh venv-Python changes that were part of this branch are dropped, since they're already on main via PR #978. Co-authored-by: Claude Opus 4.8 --- setup.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 84ba322f4..b904e8670 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,9 @@ initial admin user. Safe to re-run (skips what already exists). """ import os +import platform import shutil +import subprocess import sys BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -117,7 +119,16 @@ def create_default_admin(): print(f" Temporary password: {password}") print(f" ** Change it after first login. Set ODYSSEUS_ADMIN_PASSWORD to choose your own. **") return "created" - except ImportError: + except ImportError as e: + if "incompatible architecture" in str(e).lower(): + # bcrypt is present but built for the wrong CPU architecture — the + # same Apple Silicon mismatch check_arch() guards against, caught here + # for the rarer case of an x86 wheel inside an arm64 venv. + print(" [error] bcrypt loaded with the wrong CPU architecture.") + print(" Rebuild the venv with an arm64 Python:") + print(" rm -rf venv && /opt/homebrew/bin/python3.11 -m venv venv") + print(" ./venv/bin/pip install -r requirements.txt") + return "skipped" print(" [warn] bcrypt not installed — skipping admin user creation") print(" Run: pip install bcrypt") return "skipped" @@ -167,9 +178,52 @@ def check_deps(): print(" [ok] tmux installed") +def check_arch(): + """Stop early, with guidance, if we're on Apple Silicon but running an + Intel (x86_64) Python through Rosetta. + + A venv built with such an interpreter installs and loads compiled packages + (bcrypt, pydantic-core, onnxruntime, …) for the wrong CPU architecture, then + dies deep inside an import with a cryptic + "(mach-o file, but is an incompatible architecture)" error. Catching it here + turns that into one clear, actionable message. + """ + if sys.platform != "darwin" or platform.machine() == "arm64": + return # Not macOS, or already an arm64-native interpreter — nothing to do. + + # platform.machine() == "x86_64": either a genuine Intel Mac (fine) or an x86 + # interpreter running under Rosetta on Apple Silicon (the case we must catch). + try: + translated = subprocess.run( + ["sysctl", "-n", "sysctl.proc_translated"], + capture_output=True, text=True, timeout=5, + ).stdout.strip() + except Exception: + translated = "" + if translated != "1": + return # Genuine Intel Mac — carry on. + + print("\n [error] This is an Apple Silicon Mac, but setup is running under an") + print(" Intel (x86_64) Python through Rosetta. Compiled packages would") + print(' load as the wrong architecture and crash with "incompatible') + print(' architecture" later on.') + print("\n Rebuild the environment with Homebrew's arm64 Python:") + print(" brew install python@3.11 # if you don't have it yet") + print(" rm -rf venv") + print(" /opt/homebrew/bin/python3.11 -m venv venv") + print(" ./venv/bin/pip install -r requirements.txt") + print(" ./venv/bin/python setup.py") + print("\n Tip: ./start-macos.sh does all of this with the right Python.\n") + sys.exit(1) + + def main(): print("\n=== Odysseus Setup ===\n") + # Fail fast with a clear message if the CPU architecture is wrong (Apple + # Silicon under an x86/Rosetta Python) before importing anything native. + check_arch() + print("1. Creating directories...") create_dirs()