Skip to content

Qwen3.8 dense silently generates repeating garbage with quantized KV cache #104

Description

@phoenix-thedev

Qwen3.8 dense silently generates repeating garbage with quantized KV cache

Summary

On gmlx 0.4.6, enabling uniform 8-bit KV-cache quantization causes Qwen3.8-27B dense to generate deterministic repeating or corrupted output.

The corruption reproduces with:

  • MTP disabled
  • APC disabled
  • No custom chat template
  • A single request
  • A freshly started server

The same GGUF, prompt, and sampling configuration produces coherent output when KV quantization is disabled.

The default gmlx-owned Qwen path silently generates corrupted text. Setting GMLX_QWEN_OWNED=0 instead exposes a cache-layout exception:

Generation failed: 'tuple' object has no attribute 'shape'

This may be related to the quantized hybrid-cache extraction/merge problems discussed in mlx-vlm issue #1322, although that issue is marked fixed and covered Qwen3.6 MoE. This reproduction uses Qwen3.8 dense through gmlx.

Environment

Hardware: Apple M5 Pro, 24 GB unified memory
OS: macOS 27.0
gmlx 0.4.6
mlx 0.32.1
mlx-kquant 0.4.4
mlx-lm 0.31.3
gguf 0.19.0

gmlx doctor output:

gmlx 0.4.6 doctor
  PASS  runtime   mlx 0.32.1, mlx-kquant 0.4.4, mlx-lm 0.31.3, gguf 0.19.0, metal ok
  PASS  kernels   sdpa_vector, sdpa_decode_gqa
  WARN  config    no config found (gmlx init); searched: gmlx.yaml, ~/.config/gmlx/gmlx.yaml, ~/.gmlx.yaml
  SKIP  models    no config
  SKIP  server    no background server (gmlx serve starts one)
  PASS  launcher
  SKIP  hf token  no token
  PASS  memory    24 GB RAM
  PASS  disk

Model:

unsloth/Qwen3.8-27B-GGUF
Qwen3.8-27B-UD-Q3_K_XL.gguf

gmlx detects it as:

model_type=qwen3_5
hidden=5120
layers=64
heads=24
kv_heads=4
family=qwen3.6

Minimal reproduction

Start a server with Q8 KV, APC disabled, MTP disabled, and no custom template:

APC_ENABLED=0 \
GMLX_CACHE_LIMIT_GB=1 \
GMLX_OVERCOMMIT=1 \
KV_BITS=8 \
KV_GROUP_SIZE=64 \
MAX_KV_SIZE=4096 \
QUANTIZED_KV_START=0 \
gmlx serve \
  "/path/to/Qwen3.8-27B-UD-Q3_K_XL.gguf" \
  --host 127.0.0.1 \
  --port 8080 \
  --foreground \
  --no-menubar

Send this request:

curl -s http://127.0.0.1:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen3.8-27b",
    "messages": [{
      "role": "user",
      "content": "Explain why speculative decoding accelerates language-model inference, including one limitation, in one concise technical paragraph."
    }],
    "temperature": 1.0,
    "max_tokens": 128,
    "seed": 4242,
    "stream": false
  }' | jq

Actual result

The request succeeds at the HTTP level but returns corrupted, repetitive reasoning:

jid _  8    0  0  0  0  0  0  0  0  0  0  0  0  0  0  0...

Other Q8 runs produced patterns including:

CDATActrlctrlctrlctrlctrlctrlctrlctrl...
angep://://://://://://://://://://...

This is not a small quality regression. The output is unusable and generally repeats a token or short pattern until max_tokens.

F16 control

Start the same model without KV quantization:

GMLX_OVERCOMMIT=1 \
MAX_KV_SIZE=4096 \
gmlx serve \
  "/path/to/Qwen3.8-27B-UD-Q3_K_XL.gguf" \
  --host 127.0.0.1 \
  --port 8080 \
  --foreground \
  --no-menubar

The identical request produces coherent reasoning. Measured control performance was approximately:

Prefill: 197 tok/s
Decode: 16.8-16.9 tok/s
Peak memory: 13.36 GB

Configuration matrix

KV cache APC MTP Qwen implementation Result
F16 default off gmlx-owned Coherent
F16 default exact gmlx-owned Coherent
F16 default stochastic gmlx-owned Coherent
Q8 default exact gmlx-owned Repeating garbage
Q8 default stochastic gmlx-owned Repeating garbage
Q8 off off gmlx-owned Repeating garbage
Q8 default stochastic stock (GMLX_QWEN_OWNED=0) Server exception: tuple has no .shape

This appears to rule out MTP, stochastic sampling, APC restoration, the custom Jinja template, prompt caching, and multi-request concurrency as the sole cause.

Stock-Qwen diagnostic

With the same Q8 settings plus:

GMLX_QWEN_OWNED=0

the request fails instead of silently corrupting:

{
  "error": {
    "type": "server_error",
    "message": "Generation failed: 'tuple' object has no attribute 'shape'"
  }
}

This resembles a mismatch between a quantized cache represented as a tuple and code expecting a dense array with a .shape attribute. The gmlx-owned path appears to avoid the exception but still produces invalid model state or output.

MTP control and performance

With F16 KV, native stochastic MTP remains coherent and provides a valid speedup:

Run 1: 24.3 tok/s
Run 2: 21.7 tok/s
Run 3: 23.3 tok/s
Mean: 23.1 tok/s
Median: 23.3 tok/s
Peak memory: approximately 14.17 GB

Therefore, native MTP itself works with this model when the KV cache is not quantized.

Expected behavior

Q8 KV should produce coherent output close to the F16-KV control. If quantized KV is not currently supported for this architecture or serving path, gmlx should reject the configuration at model load rather than silently return corrupted completions.

Possible relationship

Potentially related upstream issue:

That issue describes BatchQuantizedKVCache lacking extraction and merge behavior required by Qwen's hybrid per-row decode path. The reported consequence—losing prompt-cache state, resetting the offset, and producing identical logits or repeating tokens—closely resembles this reproduction.

However, #1322 is closed and concerns Qwen3.6 MoE. This report concerns Qwen3.8 dense through gmlx 0.4.6, so it may be a separate dense-model variant, a regression in gmlx's owned Qwen forward path, an affected dependency, or a continuous-batching integration issue.

Suggested safety behavior

Until the underlying path is fixed, it may be safer to fail model loading when quantized KV is enabled for this Qwen hybrid serving path. Silent coherent-looking corruption is significantly harder to detect than an explicit unsupported-configuration error.


This report was generated by an AI and reviewed for accuracy by a human who performed the tests.

Note from human: I'm not actually all that experienced with AI haha, but that seems like what happened. If you don't want me to use AI in future replies (if you do reply) lmk and I'll stop. love the project by the way, got a pretty nice 20-30% decode boost with this!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions