Edited 2026-08-20. The original description blamed Core ML's CPU cast kernel and listed the CLIP text tower as affected. Both were wrong. The corrected analysis is below, and a self contained reproducer is in this comment. The original text is preserved at the bottom for reference.
Description
A Core ML model that applies a masked mean-pool over a dynamic sequence dimension segfaults on the first prediction whenever it actually runs on a non-CPU compute unit. The identical package on CPU_ONLY returns correct output, and compiling with a static sequence length also works.
Reproduces from plain coremltools predict, with no ExecuTorch involved.
What triggers it
Bisecting a sentence-transformers wrapper one piece at a time, over RangeDim(1, 382):
| wrapper content |
result |
encoder only (last_hidden_state) |
OK |
| encoder + CLS/SEP prepend |
OK |
| encoder + masked mean-pool |
SIGSEGV |
The mean-pool is the trigger, not the encoder:
mask = attention_mask.unsqueeze(-1).expand(tokens.size()).float()
pooled = torch.sum(tokens * mask, 1) / torch.clamp(mask.sum(1), min=1e-9)
A ~20 line standalone model doing only this pattern reports, on a non-CPU path:
Espresso exception: "Invalid blob shape": Data-dependent shapes were disabled: var_39 - [?, 768]
Core ML infers the pooled output as data dependent [?, H] even though the reduction removes the dynamic axis and the true shape is [1, H]. In a small graph the backend rejects it and falls back. In a full encoder it segfaults.
Which compute paths fail
all-mpnet-base-v2 plus masked mean-pool, RangeDim(1, 382):
| compute_units |
precision |
result |
CPU_ONLY |
fp16 |
OK |
CPU_ONLY |
fp32 |
OK |
CPU_AND_NE |
fp16 |
SIGSEGV |
CPU_AND_NE |
fp32 |
OK, but only because fp32 cannot run on the ANE and falls back to CPU |
ALL |
fp16 |
SIGSEGV |
ALL |
fp32 |
SIGSEGV |
On the CPU path the output is exact against the torch reference: cosine 1.000000, max abs 0.000000 at L = 2, 8, 37, 128, 380.
Reproducing caveat: the compiled model cache silently serves an earlier compilation and will fake a pass. Clear it between runs.
Environment
coremltools 9.0, macOS 15 arm64 (M4), Python 3.10, torch 2.13.0. ExecuTorch 1.4.1 used only to produce the larger reproducer package; the crash itself reproduces without it.
Original description (superseded, kept for reference)
The original report attributed the crash to Core ML's CPU cast kernel, based on this stack from the failing execute():
libvDSP.dylib vDSP_vflt32
Espresso Espresso::cast_kernel_cpu::__launch(...)
Espresso Espresso::layer::__launch(...)
Espresso Espresso::interpreter_t::__launch_function(...)
Espresso Espresso::net::__forward(...)
Espresso EspressoLight::espresso_plan::dispatch_task_on_compute_batch(...)
Espresso espresso_plan_execute_sync
CoreML -[MLNeuralNetworkEngine executePlan:error:]
It reported all-mpnet-base-v2 and the openai/clip-vit-base-patch32 text tower as both segfaulting with a dynamic sequence dim and working with a static one, and hypothesised that the cause was integer tensors whose extent depends on the symbolic sequence dim (mpnet's relative position buckets, CLIP's causal mask and argmax gather). That hypothesis was explicitly flagged as unconfirmed, and it did not survive bisection: the encoder including that arithmetic is fine, and the CLIP text tower is not affected at all.
Description
A Core ML model that applies a masked mean-pool over a dynamic sequence dimension segfaults on the first prediction whenever it actually runs on a non-CPU compute unit. The identical package on
CPU_ONLYreturns correct output, and compiling with a static sequence length also works.Reproduces from plain
coremltoolspredict, with no ExecuTorch involved.What triggers it
Bisecting a sentence-transformers wrapper one piece at a time, over
RangeDim(1, 382):last_hidden_state)The mean-pool is the trigger, not the encoder:
A ~20 line standalone model doing only this pattern reports, on a non-CPU path:
Core ML infers the pooled output as data dependent
[?, H]even though the reduction removes the dynamic axis and the true shape is[1, H]. In a small graph the backend rejects it and falls back. In a full encoder it segfaults.Which compute paths fail
all-mpnet-base-v2plus masked mean-pool,RangeDim(1, 382):CPU_ONLYCPU_ONLYCPU_AND_NECPU_AND_NEALLALLOn the CPU path the output is exact against the torch reference: cosine 1.000000, max abs 0.000000 at L = 2, 8, 37, 128, 380.
Reproducing caveat: the compiled model cache silently serves an earlier compilation and will fake a pass. Clear it between runs.
Environment
coremltools 9.0, macOS 15 arm64 (M4), Python 3.10, torch 2.13.0. ExecuTorch 1.4.1 used only to produce the larger reproducer package; the crash itself reproduces without it.
Original description (superseded, kept for reference)
The original report attributed the crash to Core ML's CPU cast kernel, based on this stack from the failing
execute():It reported
all-mpnet-base-v2and theopenai/clip-vit-base-patch32text tower as both segfaulting with a dynamic sequence dim and working with a static one, and hypothesised that the cause was integer tensors whose extent depends on the symbolic sequence dim (mpnet's relative position buckets, CLIP's causal mask and argmax gather). That hypothesis was explicitly flagged as unconfirmed, and it did not survive bisection: the encoder including that arithmetic is fine, and the CLIP text tower is not affected at all.