GLM-5.2 quantization crash and sparse DSA indexer loading#2
Open
csabakecskemeti wants to merge 3 commits into
Open
GLM-5.2 quantization crash and sparse DSA indexer loading#2csabakecskemeti wants to merge 3 commits into
csabakecskemeti wants to merge 3 commits into
Conversation
…ocks
Models like GLM-5.2 (glm-dsa arch) and BailingMoE2 have nextn/MTP
prediction blocks appended after the main transformer layers. These
extra blocks share the same blk.%d.ffn_*_exps tensor naming convention
and are present in the GGUF file, but n_layer() excludes them:
n_layer() = n_layer_all - n_layer_nextn
For GLM-5.2: block_count=79, nextn_predict_layers=1, so n_layer()=78.
The MoE path in layer_info() parses the block index from the tensor
name via sscanf. When it hits blk.78.ffn_down_exps.weight it gets
i_layer=78, which fails the check i_layer >= n_layer(78) with:
"Bad layer 78 for tensor blk.78.ffn_down_exps.weight. Must be in [0, 78)"
Fix: use n_layer_all (79) instead of n_layer() (78) when initializing
the FFN layer counters, so nextn block tensors are within valid range.
Regressed in ggml-org#24060 (2026-06-05) which changed hparams.n_layer from a
raw field equal to n_layer_all into n_layer() = n_layer_all - n_layer_nextn.
Quantizations of GLM-5.2 made before that commit were unaffected because
the old hparams.n_layer was 79 (= n_layer_all).
Assisted-by: Claude Sonnet
GLM-5.2 only has DSA lightning indexer tensors on a subset of blocks: the 3 dense-lead blocks (0-2) and every 4th MoE block (6, 10, 14, ..., 78). The remaining MoE blocks have no indexer tensors at all. glm-dsa.cpp required them on every block, so loading failed with: "missing tensor 'blk.3.indexer.k_norm.weight'" Two changes: - glm-dsa.cpp: mark all 5 indexer tensor creates as TENSOR_NOT_REQUIRED - deepseek32.cpp: guard the lightning indexer block with a null check on indexer_attn_q_b; when absent, top_k stays nullptr and build_attn_mla falls back to full attention for that layer Regressed in ggml-org#23346 (2026-05-29) which wired the DSA lightning indexer into the shared deepseek2 graph used by GLM_DSA. Before that PR the indexer tensors were loaded but never accessed in the graph. Assisted-by: Claude Sonnet
GLM-5.2 (and other recent models) reference TokenizersBackend in their tokenizer_config.json. This class was introduced in transformers 5.0 and does not exist in 4.x, causing AutoTokenizer.from_pretrained() to abort with "TokenizersBackend does not exist" during GGUF conversion. The previous pin (==4.57.6) prevented installation of 5.x even when explicitly requested. Relaxing to >=5.0.0 lets users install the version required by their model without needing a manual override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two bugs found while quantizing zai-org/GLM-5.2 to GGUF on current master.
Bug 1 — Quantization crash:
Bad layer N for tensor blk.N.ffn_down_exps.weight. Must be in [0, N)Root cause
PR #24060 (merged ~June 5 2026) refactored
hparams.n_layerinto a method that subtracts nextn/MTP prediction layers:In src/llama-quant.cpp, init_quantize_state_counters() uses this value to size the FFN layer counter arrays:
GLM-5.2 has n_layer_all = 80 and n_layer_nextn = 1, so n_layer() = 79. But the GGUF file contains FFN tensors for all 80 blocks (including the nextn block), so when the quantizer processes block 79 it finds the array is only sized to 79 — hence Bad layer 79.
This bug affects any model with nextn_predict_layers > 0 (GLM-5.2, BailingMoE2, any future MTP model). The same issue was independently reported upstream as ggml-org#24379.
Fix
Bug 2 — Model loading crash: missing tensor 'blk.3.indexer.k_norm.weight'
Root cause
GLM-5.2 uses a Dense Sparse Attention (DSA) lightning indexer, but only in a sparse subset of layers. The selection is controlled by two model config parameters:
This gives 22 out of 79 blocks with indexer tensors: [0, 1, 2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78]. The source model safetensors contain exactly 110 indexer tensors (22 blocks × 5 tensor types), confirming this is by design — not an incomplete download.
The load_arch_tensors in glm-dsa.cpp iterates all 79 blocks and calls create_tensor for the 5 indexer tensors on every block without TENSOR_NOT_REQUIRED. For blocks that don't have indexers (e.g. block 3), this triggers a hard "missing tensor" abort.
Additionally, the graph builder in deepseek32.cpp enters the // lightning indexer block unconditionally, which would dereference null layer pointers on any non-indexer block.
Why Unsloth's GGUF doesn't have this problem
unsloth/GLM-5.2-GGUF avoids the crash by duplicating indexer tensors into all 79 blocks, padding the missing 57 blocks with copies of nearby tensors. This was noted in issue ggml-org#24730 by @artraze: "Unsloth likely duplicated index tensors for cached layers rather than proper index caching." That approach inflates GGUF size and masks the real design intent of the model.
Fix
src/models/glm-dsa.cpp — mark all 5 indexer tensor creates as TENSOR_NOT_REQUIRED:
src/models/deepseek32.cpp — guard the lightning indexer block with a null check:
top_k is initialized to nullptr before this block. When the indexer is absent, top_k = nullptr causes build_attn to fall back to full attention — semantically correct since those blocks were never meant to use sparse selection.
Note on full DSA support
A proper fused GGML_OP_LIGHTNING_INDEXER is tracked in upstream PR ggml-org#24231 (open, by @fairydreaming), which reduces compute buffer from ~168 GB to ~5.8 GB. The fixes here are about correctness (model loads and runs), not full DSA performance.
Test
Quantized zai-org/GLM-5.2 → Q4_K_M using this branch:
Used: transformers-5.12.1(probably transformers>=5.0.0 is enough)