🐞 Bug
MLComputePlan.load_from_path() hard-crashes the whole host process (SIGABRT, uncatchable from Python) when given an .mlpackage path instead of a compiled .mlmodelc. A wrong-path-type mistake should raise a Python exception, not kill the process.
To Reproduce (100% deterministic)
from coremltools.models.compute_plan import MLComputePlan
from coremltools import ComputeUnit
# any UNCOMPILED .mlpackage path (the API expects a compiled .mlmodelc)
MLComputePlan.load_from_path(path="Any.mlpackage", compute_units=ComputeUnit.ALL)
# → libc++abi: terminating due to uncaught exception of type
# std::__1::ios_base::failure: Failed to open file: .../coremldata.bin.
# It is not a valid .mlmodelc file.
# → SIGABRT (whole process, no Python exception possible)
What happens (from the .ips crash report)
The abort happens on CoreML's internal dispatch queue, so the Python caller (blocked on a semaphore waiting for the completion handler) can never catch it:
Triggered by Thread: 10, Dispatch Queue: com.apple.coreml.MLModelAssetResourceFactory.structureLoadQueue
Thread 10 Crashed:
8 libc++abi __cxa_throw
9 CoreML Archiver::_IArchiveDiskImpl::_IArchiveDiskImpl(std::string const&, Archiver::FileFormat) + 1324
10 CoreML IArchive::IArchive(...) + 104
11 CoreML -[MLModelAssetResourceFactoryOnDiskImpl modelStructureWithError:] + 228
12 CoreML __67-[MLModelAssetResourceFactory modelStructureWithCompletionHandler:]_block_invoke + 60
... _dispatch_call_block_and_release / _dispatch_lane_serial_drain ...
Thread 0 (main):
0 libsystem_kernel semaphore_wait_trap
3 libcoremlpython.so ...
The C++ exception thrown by IArchive's constructor escapes -[MLModelAssetResourceFactoryOnDiskImpl modelStructureWithError:] (which has an NSError** out-param that should carry this error) on the structureLoadQueue, where nothing catches it → std::terminate → abort().
Expected behavior
load_from_path should either (a) accept .mlpackage and compile it internally, or (b) validate the path and raise a normal Python ValueError before calling into the native API. (The framework-side issue — the C++ exception crossing a dispatch queue instead of honoring the NSError contract — has been reported to Apple via Feedback Assistant separately.)
System environment
- coremltools version: 9.0
- OS: macOS 26.5.2 (25F84), MacBook Pro M2 Pro 16 GB (Mac14,9)
- Python 3.12.10 (python.org framework build)
- CoreML.framework CFBundleVersion 3520.5.1
Additional context
Found while using MLComputePlan to enumerate per-op compute-device support for a large mlprogram. Workaround: always pre-compile via coremltools.models.utils.compile_model() and pass the resulting .mlmodelc — and run any MLComputePlan probing in a sacrificial subprocess.
🐞 Bug
MLComputePlan.load_from_path()hard-crashes the whole host process (SIGABRT, uncatchable from Python) when given an.mlpackagepath instead of a compiled.mlmodelc. A wrong-path-type mistake should raise a Python exception, not kill the process.To Reproduce (100% deterministic)
What happens (from the .ips crash report)
The abort happens on CoreML's internal dispatch queue, so the Python caller (blocked on a semaphore waiting for the completion handler) can never catch it:
The C++ exception thrown by
IArchive's constructor escapes-[MLModelAssetResourceFactoryOnDiskImpl modelStructureWithError:](which has anNSError**out-param that should carry this error) on thestructureLoadQueue, where nothing catches it →std::terminate→abort().Expected behavior
load_from_pathshould either (a) accept.mlpackageand compile it internally, or (b) validate the path and raise a normal PythonValueErrorbefore calling into the native API. (The framework-side issue — the C++ exception crossing a dispatch queue instead of honoring theNSErrorcontract — has been reported to Apple via Feedback Assistant separately.)System environment
Additional context
Found while using
MLComputePlanto enumerate per-op compute-device support for a large mlprogram. Workaround: always pre-compile viacoremltools.models.utils.compile_model()and pass the resulting.mlmodelc— and run anyMLComputePlanprobing in a sacrificial subprocess.