Summary
parse_gguf_config (python/freetoken/models/gemma4/gguf.py) unconditionally requires
gemma4.expert_count and hardcodes moe_enabled=True, so a dense Gemma-4 GGUF cannot
load at all — the gemma4 GGUF path structurally cannot represent a non-MoE checkpoint.
docs/models.md lists dense members of the family (google/gemma-4-12B-it,
nvidia/Gemma-4-31B-IT-NVFP4) under Gemma-4, and GGUF is documented as the one native
non-safetensors path, so dense Gemma-4 GGUFs read as in scope.
Reproduction
ft serve --model gemma-4-31B-it-qat-UD-Q4_K_XL.gguf
(unsloth/gemma-4-31B-it-qat-GGUF, a dense QAT checkpoint.)
Traceback
File "python/freetoken/engine/engine.py", line 1307, in freetoken.engine.engine._adjust_config
File "freetoken/engine/config.py", line 94, in model_config
return parse_config(self.hf_config)
File "python/freetoken/models/gemma4/gguf.py", line 108, in freetoken.models.gemma4.gguf.parse_gguf_config
File "python/freetoken/models/gemma4/gguf.py", line 57, in freetoken.models.gemma4.gguf.parse_gguf_config.g
KeyError: 'missing GGUF metadata key gemma4.expert_count'
The frontend then reports Backend supervisor: KeyError and shuts the API server down
(Backend worker is gone and cannot be restarted), exit code 15.
Metadata
Reading the GGUF KV table directly, the dense checkpoint simply has no expert keys, while
the MoE sibling does:
| key |
gemma-4-31B-it-qat (dense) |
gemma-4-26B-A4B-it (MoE) |
gemma4.block_count |
60 |
30 |
gemma4.context_length |
262144 |
262144 |
gemma4.expert_count |
absent |
128 |
gemma4.expert_used_count |
absent |
8 |
gemma4.expert_feed_forward_length |
absent |
704 |
Cause
def g(key: str):
val = m.get(f"gemma4.{key}")
if val is None:
raise KeyError(f"missing GGUF metadata key gemma4.{key}")
return val
...
num_experts=int(g("expert_count")),
num_experts_per_tok=int(g("expert_used_count")),
moe_intermediate_size=int(g("expert_feed_forward_length")),
...
moe_enabled=True, # <-- unconditional
expert_quant="q4_0", # <-- unconditional
g() is fatal-on-missing by construction, and the three expert fields plus moe_enabled
assume every gemma4 GGUF is MoE.
Suggested fix
Make the expert block conditional on the metadata actually present:
is_moe = m.get("gemma4.expert_count") is not None
and populate num_experts / num_experts_per_tok / moe_intermediate_size / moe_enabled
from that, leaving the dense path to feed_forward_length (already read above).
Relationship to existing issues
Environment
- FreeToken 0.1.2 (Desktop, native Windows venv at
AppData\Local\FreeToken\venv, Python 3.12)
- RTX 5090 (sm_120, 32 GB), driver 616.56, CUDA 13
- Windows 11 26200
Summary
parse_gguf_config(python/freetoken/models/gemma4/gguf.py) unconditionally requiresgemma4.expert_countand hardcodesmoe_enabled=True, so a dense Gemma-4 GGUF cannotload at all — the gemma4 GGUF path structurally cannot represent a non-MoE checkpoint.
docs/models.mdlists dense members of the family (google/gemma-4-12B-it,nvidia/Gemma-4-31B-IT-NVFP4) under Gemma-4, and GGUF is documented as the one nativenon-safetensors path, so dense Gemma-4 GGUFs read as in scope.
Reproduction
(
unsloth/gemma-4-31B-it-qat-GGUF, a dense QAT checkpoint.)Traceback
The frontend then reports
Backend supervisor: KeyErrorand shuts the API server down(
Backend worker is gone and cannot be restarted), exit code 15.Metadata
Reading the GGUF KV table directly, the dense checkpoint simply has no expert keys, while
the MoE sibling does:
gemma-4-31B-it-qat(dense)gemma-4-26B-A4B-it(MoE)gemma4.block_countgemma4.context_lengthgemma4.expert_countgemma4.expert_used_countgemma4.expert_feed_forward_lengthCause
g()is fatal-on-missing by construction, and the three expert fields plusmoe_enabledassume every
gemma4GGUF is MoE.Suggested fix
Make the expert block conditional on the metadata actually present:
and populate
num_experts/num_experts_per_tok/moe_intermediate_size/moe_enabledfrom that, leaving the dense path to
feed_forward_length(already read above).Relationship to existing issues
attention.head_count_kv), but adifferent key and a different failure; for a dense checkpoint this one is the next blocker
behind it.
type hardcoded to Q6_K).
Environment
AppData\Local\FreeToken\venv, Python 3.12)