You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Eval bug: Nemotron 3.5 Bolt 74B-A9B segfaults on Metal in ggml_metal_op_mul_mat_id #30610
The model loads fine with #30520, but the server segfaults on the first request, during prompt processing, in ggml_metal_op_mul_mat_id (a null pipeline passed to ggml_metal_pipeline_max_theads_per_threadgroup).
Then any /v1/chat/completions request (a ~150-token user prompt here) crashes the process.
Expert tensors are ffn_up_exps/ffn_down_exps IQ2_XXS, ffn_latent_up/ffn_latent_down Q8_0, ssm_in/ssm_out NVFP4. MoE layers have different expert counts per layer (240/304/368/432/496), with expert_count = 496 in the header.
Server log ends right after the request is picked up:
I srv llama_server: model loaded
I srv llama_server: listening on http://127.0.0.1:8080
I slot get_availabl: id 0 | task -1 | selected slot by LRU, n_tokens = 0, t_last = -1
I slot launch_slot_: id 0 | task 0 | processing task, is_child = 0
Crash report (EXC_BAD_ACCESS, SIGSEGV, KERN_INVALID_ADDRESS at 0x0), main thread:
The null pipeline is the mul_mm_id map0 kernel. Bolt routes to 9 experts per token (n_expert_used = 9), but the map0 kernel is only instantiated for ne20 ∈ {1, 2, 4, 5, 6, 8, 10, 16, 22}:
ggml_metal_library_get_pipeline_mul_mm_id_map0() looks up kernel_mul_mm_id_map0_ne20_9, which doesn't exist, so it returns a null pipeline. The next line dereferences it in ggml_metal_pipeline_max_theads_per_threadgroup():
That matches your backtrace. It also explains why the crash happens during prompt processing: map0 only runs on the batched mul_mm_id path, and single-token decode goes through mul_mv_id, which isn't specialized on ne20.
The per-layer expert counts (240–496) and the IQ2_XXS/NVFP4 types aren't the trigger. This should reproduce with any quant of Bolt. Nemotron 3.5 Super (top-22) already has a kernel.
Likely fix: add a <9> instantiation next to the others, as #18667 did for ne20=5. It might also be worth having supports_op (or the map0 getter) reject an uninstantiated ne20, so the next odd top-k falls back instead of segfaulting.
(I found this by reading the source. I haven't tested the patch on hardware.)
Confirmed the <9> fix on an M3 Ultra (96 GB), built at f12aaa3. A second problem then shows up with NVIDIA's 24GB GGUF: decode runs at ~6 t/s on Metal.
The cause is that the file stores the 80 Mamba ssm_in / ssm_out projections as NVFP4. ggml_metal_supports_mul_mat_op returns false for GGML_TYPE_NVFP4, so those matmuls fall back to CPU on every token. The IQ2_XXS experts themselves are fine.
Workaround: rewrite the file, converting only the NVFP4 tensors to Q8_0. The QAT IQ2_XXS experts, the Q8_0 tensors and the F32 .scale tensors are copied byte-for-byte. The worst conversion error was 0.41% of a tensor's range, and the file grows from 22.0 to 24.2 GB. It takes about 2 minutes with gguf-py:
On a small set of checks (tool call and round trip, strict JSON schema, a code task with hidden tests, a reasoning trap), the rebuilt file passed everything the original did.
A native Metal NVFP4 mul_mat / mul_mat_id would make this unnecessary. Until then, it may be worth noting on the model card that Apple Silicon users should convert those tensors.
Activity
nazeshinjite commentedon Oct 15, 2026
The null pipeline is the
mul_mm_idmap0 kernel. Bolt routes to 9 experts per token (n_expert_used = 9), but the map0 kernel is only instantiated forne20∈ {1, 2, 4, 5, 6, 8, 10, 16, 22}:llama.cpp/ggml/src/ggml-metal/kernels/mul_mm.metal
Lines 590 to 598 in b76b133
ggml_metal_library_get_pipeline_mul_mm_id_map0()looks upkernel_mul_mm_id_map0_ne20_9, which doesn't exist, so it returns a null pipeline. The next line dereferences it inggml_metal_pipeline_max_theads_per_threadgroup():llama.cpp/ggml/src/ggml-metal/ggml-metal-ops.cpp
Lines 2914 to 2918 in b76b133
That matches your backtrace. It also explains why the crash happens during prompt processing: map0 only runs on the batched
mul_mm_idpath, and single-token decode goes throughmul_mv_id, which isn't specialized onne20.The per-layer expert counts (240–496) and the IQ2_XXS/NVFP4 types aren't the trigger. This should reproduce with any quant of Bolt. Nemotron 3.5 Super (top-22) already has a kernel.
Likely fix: add a
<9>instantiation next to the others, as #18667 did forne20=5. It might also be worth havingsupports_op(or the map0 getter) reject an uninstantiatedne20, so the next odd top-k falls back instead of segfaulting.(I found this by reading the source. I haven't tested the patch on hardware.)
LiahMartens commentedon Oct 15, 2026
@nazeshinjite can confirm that works
ashe-wb commentedon Oct 15, 2026
Confirmed on M1 Max: adding the
<9>map0 instantiation fixes the crash. Bolt IQ2_XXS now answers normally.itsandyking commentedon Oct 16, 2026
Confirmed the
<9>fix on an M3 Ultra (96 GB), built at f12aaa3. A second problem then shows up with NVIDIA's 24GB GGUF: decode runs at ~6 t/s on Metal.The cause is that the file stores the 80 Mamba
ssm_in/ssm_outprojections as NVFP4.ggml_metal_supports_mul_mat_opreturns false forGGML_TYPE_NVFP4, so those matmuls fall back to CPU on every token. The IQ2_XXS experts themselves are fine.Workaround: rewrite the file, converting only the NVFP4 tensors to Q8_0. The QAT IQ2_XXS experts, the Q8_0 tensors and the F32
.scaletensors are copied byte-for-byte. The worst conversion error was 0.41% of a tensor's range, and the file grows from 22.0 to 24.2 GB. It takes about 2 minutes with gguf-py:PYTHONPATH=llama.cpp/gguf-py python3 nvfp4_to_q8.py NVIDIA-Nemotron-3.5-Bolt-74B-A9B-24GB.gguf Bolt-24GB-mamba-q8.ggufM3 Ultra,
llama-bench -fa 1 -r 3:On a small set of checks (tool call and round trip, strict JSON schema, a code task with hidden tests, a reasoning trap), the rebuilt file passed everything the original did.
A native Metal NVFP4
mul_mat/mul_mat_idwould make this unnecessary. Until then, it may be worth noting on the model card that Apple Silicon users should convert those tensors.Caladrel65 commentedon Oct 16, 2026
Confirmed on an M5 Max as well that the <9> line makes it run. Getting around 11-12 tok/s.
IMO the unsupported quant type is a separate issue, but one worth creating.
Add Nemotron 3.5 Bolt 74B-A9B
Add Nemotron 3.5 Bolt 74B-A9B (#754)
metal : fix missing MoE kernel variant for 9 experts per token (#30610)…
metal : fix missing MoE kernel variant for 9 experts per token (ggml-…
metal : fix missing MoE kernel variant for 9 experts per token (ggml-…