Skip to content

model : load hparams.n_layer_nextn before n_layer() calls - #28159

Merged
CISC merged 2 commits into
masterfrom
cisc/load-hparams-nextn-early
Sep 1, 2026
Merged

model : load hparams.n_layer_nextn before n_layer() calls#28159
CISC merged 2 commits into
masterfrom
cisc/load-hparams-nextn-early

Conversation

@CISC

@CISC CISC commented Sep 1, 2026

Copy link
Copy Markdown
Member

Overview

Fixes inconsistent return values from n_layer().

Additional information

This would f.ex. break loading of models with n_head_kv arrays < n_layer_all.

Requirements

  • I have read and agree with the contributing guidelines
  • AI usage disclosure: Ca
  • Language disclosure: Jalisco Nahuatl

@github-actions github-actions Bot added the model Model specific label Sep 1, 2026
@CISC
CISC requested a review from ggerganov September 1, 2026 11:16
@CISC
CISC merged commit 9d81721 into master Sep 1, 2026
22 of 26 checks passed
@CISC
CISC deleted the cisc/load-hparams-nextn-early branch September 1, 2026 11:55
eauchs added a commit to eauchs/llama.cpp that referenced this pull request Sep 1, 2026
…l-org#28159 hoist

ggml-org#28159 moved the n_layer_nextn read into load_hparams, so n_layer()
excludes the NextN block by the time the base fills n_head_arr,
n_head_kv_arr and n_ff_arr: the per-layer arrays stop before it and its
entries stay zero. Harmless for the archs that only build trunk tensors,
fatal as soon as a context reduces to that layer: the MTP context sized
its KV cache from n_head_kv == 0 and died on "failed to allocate buffer
for kv cache".

The entries are copied from the last trunk layer, which is what a scalar
key would have broadcast anyway. The local read of the key (lines 27 29)
becomes a duplicate of the base and disappears with it.
@CISC

CISC commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

Hmmm, this may have broken Step3.5

@eauchs

eauchs commented Sep 1, 2026

Copy link
Copy Markdown

@CISC first: thank you for the hoist. It also removed the duplicated read in every NextN arch, this one included, and the local copy is gone from my branch as a result. What follows is a side effect of it, not a complaint; you could not see it from mainline, because no arch there exposes the case.

After the hoist, n_layer() reads n_layer_all - n_layer_nextn from line 1226, so the three get_key_or_arr at lines 1287, 1288 and 1296 stop before the NextN block, and its entries stay zero. That is invisible for the archs that only build trunk tensors, since they never read the block's hparams. It is fatal as soon as a context reduces to that layer, which is what the glm5next MTP draft does: measured on my branch, on top of 9d81721, n_head_kv[3] = 1 against [4] = 0 (n_ff[3] = 192, [4] = 0, n_layer_all = 5, nextn = 1) — the context reduces to that single layer and sized its KV cache from n_head_kv == 0, dying on failed to allocate buffer for kv cache.

So the question I could not settle from mainline alone: after the hoist, should the base broadcast the last value onto the NextN entries, or is it each arch's job to mirror it? A scalar key would have broadcast anyway, which is what my branch does now, in one small commit on top of yours — enough for this arch, but if you would rather the base own it, say so and the mirror is one delete away.

@Sciguy429

Copy link
Copy Markdown

Quick regression note:

This appears to have broken model loading for GLM5-Next:

llama_model_loader: - type  f32:  740 tensors
llama_model_loader: - type bf16:  672 tensors
llama_model_quantize: failed to quantize: key glm5-next.attention.head_count_kv has wrong array length; expected 45, got 46
llama_quantize: failed to quantize model from '/mnt/c/Users/(REMOVED)/Downloads/GLM/GLM-5-00001-of-00006.gguf'
0.00.281.658 I print_info: file format = GGUF V3 (latest)
0.00.281.658 I print_info: file type   = Q8_0
0.00.281.660 I print_info: file size   = 129.78 GiB (3.48 BPW)
0.00.281.705 I llama_prepare_model_devices: using device CUDA0 (NVIDIA GeForce RTX 4090) (0000:01:00.0) - 23660 MiB free
0.00.283.748 E llama_model_load: error loading model: error loading model hyperparameters: key glm5-next.attention.head_count_kv has wrong array length; expected 45, got 46
0.00.283.749 E llama_model_load_from_file_impl: failed to load model
0.00.283.754 E cmn  common_init_: failed to load model '/mnt/NVME-Storage/LLM/Models/GLM5.3-Flash/GLM-5.3-Flash-UNCENSORED-IQ3.gguf'
0.00.283.758 E srv    load_model: failed to load model, '/mnt/NVME-Storage/LLM/Models/GLM5.3-Flash/GLM-5.3-Flash-UNCENSORED-IQ3.gguf'
0.00.283.760 I srv    operator(): operator(): cleaning up before exit...
0.00.284.493 E srv  llama_server: exiting due to model loading error

As far as I can tell, this breaks ALL current gguf releases.

@CISC

CISC commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

@eauchs @Sciguy429 Yeah, but also Step3.5, I think we do indeed have to include full array, however there may be other breakages the other way, trying to figure it out now.

@0cc4m

0cc4m commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

This broke Qwen 3.8 27B MTP.

@ggerganov

Copy link
Copy Markdown
Member

This broke Qwen 3.8 27B MTP.

Fixed with #28173

@ZacharyReis

Copy link
Copy Markdown

This does not fully cover the gemma4-assistant arch. Two things still break Gemma 4 MTP draft heads with this PR applied.

1. The assert added in 9d81721 fires before any of these loads are reached.

llama-model.cpp:1227: GGML_ASSERT(hparams.n_layer_nextn < hparams.n_layer_all);

Gemma 4 assistant/drafter models are standalone draft heads whose entire body is the nextn stack, so nextn_predict_layers == block_count by construction.

All three heads I have tested are 4/4:

gemma-4-E4B-it-BF16-MTP.gguf                 block_count 4, nextn_predict_layers 4
gemma-4-12B-it-qat-assistant-MTP-Q8_0.gguf   block_count 4, nextn_predict_layers 4
mtp-google_gemma-4-26B-A4B-it-Q8_0.gguf      block_count 4, nextn_predict_layers 4

They abort in load_hparams before reaching the array loads. This is also the condition the arch-specific assert removed in 9d81721 used to require (n_layer_nextn == n_layer_all). Relaxing to <= works and still catches nextn > block_count.

2. gemma4-assistant.cpp:7 also sizes an array by n_layer(), which is 0 here.

ml.get_key_or_arr(LLM_KV_ATTENTION_SLIDING_WINDOW_PATTERN, hparams.is_swa_impl, hparams.n_layer());

With the assert relaxed but only the three loads in this PR changed, the E4B head gets past head_count_kv and then fails on:

error loading model hyperparameters: key gemma4-assistant.attention.sliding_window_pattern has wrong array length; expected 0, got 4

(The 26B-A4B head fails earlier, on head_count_kv, which this PR does fix.)

Repro on master, no server config needed - load any Gemma 4 assistant head directly as the main model:

llama-server -m gemma-4-E4B-it-BF16-MTP.gguf

Result with both changes plus this PR: --model-draft and --spec-draft-model work again. Tested on E4B and 26B-A4B, draft acceptance 0.54 to 0.86.

@CISC

CISC commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

@ZacharyReis Ah, nice catch, I'll make a separate fix for that.

@CISC

CISC commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

(The 26B-A4B head fails earlier, on head_count_kv, which this PR does fix.)

Am I reading correctly that this model was in fact broken before this change? IE, that its n_head_kv array is smaller than n_layer_all?

@ZacharyReis

Copy link
Copy Markdown

@CISC the answer is no. The array is exactly n_layer_all, not smaller, and the model was not broken before this change.

I had Opus dump the per-layer array lengths from all three MTP heads:

head block_count nextn head_count_kv swa_pattern
E4B 4 4 scalar 2 array[4]
12B 4 4 array[4] [8,8,8,1] array[4]
26B-A4B 4 4 array[4] [8,8,8,2] array[4]

Opus output below:

The 26B carries 4 entries, equal to n_layer_all, not below it.

Before 9d81721, n_layer_nextn was still 0 when the generic loader reached that read, because gemma4-assistant.cpp set it afterwards in load_arch_hparams. So n_layer() returned 4, matching the array. The hoist makes n_layer() evaluate to 4 - 4 = 0 at that same read, so the expected length collapses to 0 while the file still correctly carries 4. #28173 restores the length that was already in effect.

Two things that may be useful, since they need these specific files to see:

The scalar/array split is why my two nodes failed at different keys. E4B's head_count_kv is uniform, so it was written as a scalar and broadcasts to any length, including 0. It passes that read and dies later on sliding_window_pattern. The 12B and 26B have a differing last layer, so they were written as real arrays and die earlier on head_count_kv. That is @eauchs's "uniform values go out as a scalar key" showing up in practice.

These files are also in the set #28173 repairs, not the set widening could break. eauchs's exposed set was files carrying trunk-length arrays; these carry n_layer_all-length arrays.

I have been running the equivalent of #28173 + #28183 together since about 15:00 UTC on all three heads. Both nodes load and serve with MTP active, and draft acceptance is back in its normal band (E4B runs 0.54-0.65 over real traffic). #28183 as written matches what I patched locally, line for line.

AI usage disclosure: yes. I used Claude Opus to diagnose the regression, dump the GGUF metadata above, and draft this comment. I reviewed it, I am running the patch, and the numbers are from my own machines.

@dan1j3l

dan1j3l commented Sep 1, 2026

Copy link
Copy Markdown

This commit also appear to broke mtp loading on gemma4 models:

53739] 0.03.126.460 I common_speculative_init_result: loading draft model '/mnt/storage/llm/Gemma-4-12B/mtp-gemma-4-12B-it.gguf'
[53739] /home/danijel/Development/llama.cpp/src/llama-model.cpp:1227: GGML_ASSERT(hparams.n_layer_nextn < hparams.n_layer_all) failed
[53739] [New LWP 6700]
[53739] [New LWP 6691]
[53739] [New LWP 6690]
[53739] [New LWP 6689]
[53739] [New LWP 6688]
[53739] [New LWP 6687]
[53739] [New LWP 6686]
[53739] [New LWP 6685]
[53739] [New LWP 6684]
[53739] [New LWP 6683]
[53739] [New LWP 6682]
[53739] [New LWP 6681]
[53739] [New LWP 6680]
[53739] [New LWP 6679]
[53739] [New LWP 6678]
[53739] [New LWP 6677]
[53739] [New LWP 6676]
[53739] [New LWP 6675]
[53739] [New LWP 6674]
[53739] [New LWP 6673]
[53739]
[53739] This GDB supports auto-downloading debuginfo from the following URLs:
[53739] ima:enforcing
[53739] https://debuginfod.fedoraproject.org/
[53739] ima:ignore
[53739] Enable debuginfod for this session? (y or [n]) [answered N; input not from terminal]
[53739] Debuginfod has been disabled.
[53739] To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[53739] [Thread debugging using libthread_db enabled]
[53739] Using host libthread_db library "/lib64/libthread_db.so.1".
[53739] 0x00007f5132a82312 in __syscall_cancel_arch () from /lib64/libc.so.6
[53739] #0 0x00007f5132a82312 in __syscall_cancel_arch () from /lib64/libc.so.6
[53739] #1 0x00007f5132a7652c in __internal_syscall_cancel () from /lib64/libc.so.6
[53739] #2 0x00007f5132a76574 in __syscall_cancel () from /lib64/libc.so.6
[53739] #3 0x00007f5132ae614f in wait4 () from /lib64/libc.so.6
[53739] #4 0x0000000000f40efb in ggml_print_backtrace ()
[53739] #5 0x0000000000f41343 in ggml_abort ()
[53739] #6 0x0000000000aaa654 in llama_model_base::load_hparams(llama_model_loader&) ()
[53739] #7 0x00000000009cd5d8 in llama_model_load(gguf_context*, void ()(ggml_tensor, void*), void*, std::__cxx11::basic_string<char, std::char_traits, std::allocator > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::allocator<std::__cxx11::basic_string<char, std::char_traits, std::allocator > > >&, _IO_FILE*, llama_model_params&) ()
[53739] #8 0x00000000009cf161 in llama_model_load_from_file_impl(gguf_context*, void ()(ggml_tensor, void*), void*, std::__cxx11::basic_string<char, std::char_traits, std::allocator > const&, std::vector<std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::allocator<std::__cxx11::basic_string<char, std::char_traits, std::allocator > > >&, _IO_FILE*, llama_model_params) ()
[53739] #9 0x00000000009d0cd0 in llama_model_load_from_file ()
[53739] #10 0x00000000007ebcf9 in common_speculative_init_result::common_speculative_init_result(common_params&, llama_model*, llama_context*) ()
[53739] #11 0x00000000007ebf0c in common_speculative_init_from_params(common_params&, llama_model*, llama_context*) ()
[53739] #12 0x0000000000604b10 in server_context_impl::load_model(common_params&) ()
[53739] #13 0x00000000005579c3 in llama_server(common_params&, int, char**) ()
[53739] #14 0x0000000000559bfa in llama_server(int, char**) ()
[53739] #15 0x00007f5132a0a681 in __libc_start_call_main () from /lib64/libc.so.6
[53739] #16 0x00007f5132a0a798 in __libc_start_main_impl () from /lib64/libc.so.6
[53739] #17 0x00000000005500e5 in _start ()
[53739] [Inferior 1 (process 6667) detached]

@CISC

CISC commented Sep 1, 2026

Copy link
Copy Markdown
Member Author

Sorry for the noise everyone, the two remaining PRs will put everything back to normal (but in a more maintainable state). :)

@tha80

This comment was marked as resolved.

MarkShark2 added a commit to MarkShark2/llama.cpp that referenced this pull request Sep 2, 2026
68 upstream commits, 9 conflicted files. Brings qwen4exp fixes needed for
Qwen3.8-Flash-Next: recurrent state rollback (ggml-org#28123), seq_cp/block position
keying/mtmd input (ggml-org#27941), indexer head slicing (ggml-org#28023), and the central
n_layer_nextn load (ggml-org#28159).

Resolutions:

- ggml-rpc.cpp: resolved entirely to the fork side again. Upstream's
  rpc_dispatcher/proto-6.0 port stays deferred; the fork keeps 5.1.3. Note
  that upstream ggml-org#26500 now implements the same foreign-endpoint skip in
  serialize_graph that the fork carries, so that one is a candidate to drop
  when the dispatcher port finally happens.
- llama-graph.cpp: adopted upstream's fused ggml_swiglu_clamp for both the
  dense and MoE clamp paths and kept the fork's LLM_ARCH_GLM5NEXT in the
  condition. The fused kernel is min(gate,limit) / clamp(up,+-limit) /
  swiglu_split, i.e. bit-identical to the three-op sequence it replaces, and
  Vulkan implements it, so the boards get it too. Costs the
  ffn_*_clamped cb() trace names, same tradeoff upstream took for DSV4.
- speculative.cpp: upstream ggml-org#27310 folded the DFlash encoder into the
  injection decode and deleted features_buf. The fork's non-finite feature
  scrub moves onto batch_inject.embd in place; the last_tap_nextn tap
  automerged.
- dflash.cpp: kept the fork's Laguna kv_inp norm and deferred gated o_proj,
  layered on upstream's newly-populated wk_s/wv_s/wo_s scale arguments
  (previously NULL). The gated path latches wo_s alongside wo.
- nemotron-h.cpp: dropped the now-duplicate NEXTN_PREDICT_LAYERS get_key,
  kept the fork's stricter assert and n_layer_nextn_per_head derivation.
- llama-model-loader.cpp: kept the fork's rpc_preloaded skip ahead of
  upstream's use_mmap || lazy.has(cur) predicate.
- tests: kept both sets of flash-attn cases; ported the fork's GLM5NEXT
  test_dsa_kpool/test_mtp onto upstream's int-verbosity logging convention.

Windows CUDA build green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U3H5motr51eTWujztSXykc
Randozart added a commit to Randozart/llama.cpp that referenced this pull request Sep 4, 2026
KV restore batching (ggml-org#27991), kv-cells seq-scan early stop (ggml-org#28011),
MOE fusion to specdec + multi-token (ggml-org#27621), mm_ids_helper templated
fast path (ggml-org#27978), qwen4exp recurrent state rollback (ggml-org#28123),
n_layer_nextn load order (ggml-org#28159), FA K/V XOR-swizzle smem tiles
(ggml-org#25635), --lazy-mode -lzm (ggml-org#27837/ggml-org#27969).

TQ3/TurboQuant stack and vitriol-* integration auto-merged clean;
no conflicts. Experiment E1 of mining-experiment-master-plan-2026-09-01.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

model Model specific

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants