Skip to content

[BUG] torch probe aborts (SIGABRT) on macOS with duplicate libomp — a crash report per system-info refresh #391

Description

@prasta1

Summary

getTorchBuildInfo probes for torch by running python -c "try: import torch ... except Exception: ...". On macOS, when the probed interpreter has torch and numpy installed such that two OpenMP runtimes get loaded, import torch does not raise — it calls abort() inside libomp. A Python except Exception cannot catch SIGABRT, so the guard is bypassed, the interpreter dies, and macOS writes a crash report. Because the probe runs on a schedule, this produces a steady stream of crash reports for as long as the controller is running.

Environment

  • Controller: main @ 52c2b20 (v2.9.10)
  • Desktop app: 2.9.10
  • macOS 27.0 (26A5388g), Apple M5 Pro (arm64)
  • Probed interpreter: Homebrew Python 3.14.6 with torch and numpy installed

Affected code

controller/src/modules/system/platform/torch-info.ts:6-9

const TORCH_PROBE_ARGS = [
  "-c",
  "import json\ntry:\n import torch\n print(...)\nexcept Exception:\n print(...)",
];

What actually happens

import torch triggers validate_numpy_for_dlpack_deleter_bug(), which imports numpy, which initializes OpenBLAS. Two distinct libomp.dylib images end up mapped into the one process — one pulled in by libtorch/libc10, the other by libopenblasp via libgfortran — and the second registration aborts:

0   libsystem_kernel.dylib        __pthread_kill + 8
2   libsystem_c.dylib             abort + 148
3   libomp.dylib                  __kmp_abort_process + 52
4   libomp.dylib                  __kmp_fatal + 56
5   libomp.dylib                  __kmp_register_library_startup() + 1740
9   libomp.dylib                  omp_get_max_threads + 28
10  libopenblasp-r0.3.34.dylib    blas_thread_init + 12
11  libopenblasp-r0.3.34.dylib    gotoblas_init + 76
...
102 libtorch_python.dylib         torch::utils::validate_numpy_for_dlpack_deleter_bug() + 56
103 libtorch_python.dylib         THPVariable_initModule(_object*) + 1892
Exception Type:      EXC_CRASH (SIGABRT)
Termination Reason:  Namespace SIGNAL, Code 6, Abort trap: 6
Parent Process:      bun
Coalition:           org.local.studio.controller

Both libomp images in Binary Images, confirming the duplicate load:

0x10a5dc000 libomp.dylib <e56febf1-776c-35bb-b9a1-8c978a01425c>   # via libtorch/libc10
0x10a768000 libomp.dylib <e08eca60-8306-3d5b-8a9c-5673087c6760>   # via OpenBLAS/libgfortran

Reproduction

  1. Install torch and numpy into a macOS Python that ends up with two OpenMP runtimes (Homebrew python@3.14 with Homebrew-built torch reproduces it reliably).
  2. Install and start the controller so that interpreter is the one probed.
  3. Watch ~/Library/Logs/DiagnosticReports/Python-*.ips.

Observed: 10 crash reports in roughly five minutes, one about every 31 seconds, each with the stack above.

Impact

Functionally this degrades correctly — parseTorchBuildOutput returns EMPTY_TORCH on non-zero status, so torch info is simply reported as unavailable. The problem is the side effects:

  • A macOS crash report per probe, indefinitely. This reads to a user as "the app is crash-looping", which is how I found it.
  • On systems with crash reporting UI enabled, repeated user-visible crash notifications.
  • Torch is never detected on these systems, even though it is installed.

Suggested fix

Set KMP_DUPLICATE_LIB_OK=TRUE in the probe's child environment. It is contained to this one short-lived probe, so the usual caveats about that flag don't apply to any real compute.

runCommandAsyncEffect(python, TORCH_PROBE_ARGS, {
  timeoutMs: TORCH_PROBE_TIMEOUT_MS,
  environment: { ...process.env, KMP_DUPLICATE_LIB_OK: "TRUE" },
})

OMP_NUM_THREADS=1 alongside it would also keep the probe from spinning up a thread pool it never uses.

Verified locally: adding KMP_DUPLICATE_LIB_OK=TRUE to the controller's launchd EnvironmentVariables stopped the crashes outright — zero new reports in 100s, against one per ~31s before.

It may also be worth treating a signal-terminated probe distinctly from a clean non-zero exit, so a crashing child is logged as a probe failure rather than silently folded into "torch not installed".

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions