Summary
The mlx submodule vendored by mlx-swift (Source/Cmlx/mlx) is pinned to
ce45c52 (March 12, 2026) on both main (3a24fa3, Aug 14, 2026) and the
latest release 0.31.6 — I checked every 0.31.x tag, they all pin the same
commit.
That pin predates ml-explore/mlx PR #3523 (a025496c, "Catch error in
CommandBuffer and poison the events", merged June 13, 2026), which fixed the
uncatchable-crash class tracked in ml-explore/mlx#3224 and ml-explore/mlx#3390:
gpu::check_error throwing std::runtime_error from inside
MTL::CommandBuffer::addCompletedHandler callbacks
(mlx/backend/metal/eval.cpp, lines 63/70/80 at ce45c52). Those callbacks
run on Metal's com.Metal.CompletionQueueDispatch libdispatch queue, where no
C++ catch frame exists, so any command-buffer error terminates the process with
SIGABRT. Swift host apps cannot catch it — no withError, do/catch, or
signal-level handling helps.
As a result, every mlx-swift app today (including anything built on
mlx-swift-lm) still hard-crashes on transient Metal failures: GPU watchdog
kills, memory-pressure kernel terminations, device loss.
Real-world impact
Production macOS/Catalyst app (on-device legal document analysis, models up to
70B). Under memory pressure, the OS killed an in-flight Metal kernel; the
completion handler then aborted the whole app:
Exception Type: EXC_CRASH (SIGABRT)
Termination Reason: Namespace SIGNAL, Code 6 Abort trap: 6
abort() called
Crashed thread — Dispatch queue: com.Metal.CompletionQueueDispatch
libsystem_c.dylib abort
libc++abi.dylib demangling_terminate_handler()
libc++abi.dylib std::__terminate(void (*)())
libc++abi.dylib __cxxabiv1::failed_throw(...)
libc++abi.dylib __cxa_throw
LexPenalAI (statically linked Cmlx frame)
LexPenalAI (statically linked Cmlx frame)
Metal MTLDispatchListApply
Metal -[_MTLCommandBuffer didCompleteWithStartTime:endTime:error:]
(The two unsymbolicated app frames sit exactly between __cxa_throw and
MTLDispatchListApply; with mlx statically linked they correspond to
gpu::check_error and the completion-handler block in
mlx/backend/metal/eval.cpp — the only throw reachable from a command-buffer
completion callback at pin ce45c52.)
We can only prevent the OS-initiated kill (holding NSProcessInfo activity
assertions during generation); we cannot survive the abort if it happens.
a025496c fixes this properly: handlers no longer throw; the error poisons the
stream's events and surfaces on the calling thread, where Swift can catch it
via the existing error path.
Ask
Bump Source/Cmlx/mlx to a commit ≥ a025496c (June 13, 2026) and cut a
release. Current mlx main also includes follow-ups (693ed59
new_thread_unsafe_stream, 09ebe730 residency-set split) that may need Cmlx
build-glue updates — if a full bump to head is too churny in one step, a bump
to any commit that includes a025496c already removes the crash class.
Happy to test a branch on our workload (sustained 8B–70B generation on M-class
hardware with deliberate memory-pressure kills) — this crash is reliably
reproducible for us.
Environment
- mlx-swift
0bb916c (July 1, 2026) / also verified against main = 3a24fa3
- vendored mlx
ce45c52 (March 12, 2026)
- macOS 27.0 beta (Darwin 27), Xcode 27 beta 4, Apple Silicon
Summary
The
mlxsubmodule vendored by mlx-swift (Source/Cmlx/mlx) is pinned toce45c52(March 12, 2026) on bothmain(3a24fa3, Aug 14, 2026) and thelatest release
0.31.6— I checked every0.31.xtag, they all pin the samecommit.
That pin predates ml-explore/mlx PR #3523 (
a025496c, "Catch error inCommandBuffer and poison the events", merged June 13, 2026), which fixed the
uncatchable-crash class tracked in ml-explore/mlx#3224 and ml-explore/mlx#3390:
gpu::check_errorthrowingstd::runtime_errorfrom insideMTL::CommandBuffer::addCompletedHandlercallbacks(
mlx/backend/metal/eval.cpp, lines 63/70/80 atce45c52). Those callbacksrun on Metal's
com.Metal.CompletionQueueDispatchlibdispatch queue, where noC++ catch frame exists, so any command-buffer error terminates the process with
SIGABRT. Swift host apps cannot catch it — no
withError,do/catch, orsignal-level handling helps.
As a result, every mlx-swift app today (including anything built on
mlx-swift-lm) still hard-crashes on transient Metal failures: GPU watchdog
kills, memory-pressure kernel terminations, device loss.
Real-world impact
Production macOS/Catalyst app (on-device legal document analysis, models up to
70B). Under memory pressure, the OS killed an in-flight Metal kernel; the
completion handler then aborted the whole app:
(The two unsymbolicated app frames sit exactly between
__cxa_throwandMTLDispatchListApply; with mlx statically linked they correspond togpu::check_errorand the completion-handler block inmlx/backend/metal/eval.cpp— the onlythrowreachable from a command-buffercompletion callback at pin
ce45c52.)We can only prevent the OS-initiated kill (holding
NSProcessInfoactivityassertions during generation); we cannot survive the abort if it happens.
a025496cfixes this properly: handlers no longer throw; the error poisons thestream's events and surfaces on the calling thread, where Swift can catch it
via the existing error path.
Ask
Bump
Source/Cmlx/mlxto a commit ≥a025496c(June 13, 2026) and cut arelease. Current mlx main also includes follow-ups (
693ed59new_thread_unsafe_stream,
09ebe730residency-set split) that may need Cmlxbuild-glue updates — if a full bump to head is too churny in one step, a bump
to any commit that includes
a025496calready removes the crash class.Happy to test a branch on our workload (sustained 8B–70B generation on M-class
hardware with deliberate memory-pressure kills) — this crash is reliably
reproducible for us.
Environment
0bb916c(July 1, 2026) / also verified againstmain=3a24fa3ce45c52(March 12, 2026)