Summary
mlx_lm.server speculative decoding fails for a Qwen 35B target model with a Qwen 0.8B draft model because the prompt cache is ArraysCache, which speculative_generate_step rejects as non-trimmable.
This reproduces in both a normal draft speed smoke and a no-explicit-prompt-cache probe. Baseline generation without --draft-model works.
Related context: #872 tracks internal MTP support for Qwen/Nemotron models; this issue is specifically about external draft-model speculative decoding failing at runtime because the selected cache is not trimmable.
Environment
- macOS 26.0, arm64
- Python 3.12.11
mlx-lm==0.31.3
mlx==0.31.2
- Runtime:
mlx_lm.server
Models
Target model:
- Path:
/Volumes/Models-toolkode/models/Qwen3.6-35B-A3B-UD-MLX-4bit
- Architecture:
Qwen3_5MoeForConditionalGeneration
- Model type:
qwen3_5_moe
- 4-bit affine quantization
Draft model:
- Path:
/Volumes/Models-toolkode/models/Qwen3.5-0.8B-8bit
- Architecture:
Qwen3_5ForConditionalGeneration
- Model type:
qwen3_5
- 8-bit affine quantization
Repro
/Users/aaron/Code/training-llm/.venv-mlx/bin/mlx_lm.server \
--model /Volumes/Models-toolkode/models/Qwen3.6-35B-A3B-UD-MLX-4bit \
--draft-model /Volumes/Models-toolkode/models/Qwen3.5-0.8B-8bit \
--num-draft-tokens 5 \
--host 127.0.0.1 \
--port 18087 \
--log-level ERROR
GET /v1/models succeeds. A chat completion request then fails during generation with:
ValueError: Speculative decoding requires a trimmable prompt cache (got {'ArraysCache'}).
Trace tail:
File ".../mlx_lm/generate.py", line 716, in stream_generate
for n, (token, logprobs, from_draft) in enumerate(token_generator):
File ".../mlx_lm/generate.py", line 531, in speculative_generate_step
raise ValueError(
ValueError: Speculative decoding requires a trimmable prompt cache (got {'ArraysCache'}).
Expected
Speculative decoding should either:
- use/select a trimmable cache for this path,
- support trimming the cache type used by these Qwen hybrid-attention models, or
- fail during server startup / first validation with a clear unsupported-configuration error before accepting requests.
Actual
The server accepts startup/model requests, then the completion handler crashes during speculative generation because ArraysCache is not accepted as trimmable.
Notes
This was found while benchmarking local ToolKode worker inference. Prefix-cache-only baseline works and the same target model without --draft-model generated normally at about 44 tok/s in our speed smoke.
Summary
mlx_lm.serverspeculative decoding fails for a Qwen 35B target model with a Qwen 0.8B draft model because the prompt cache isArraysCache, whichspeculative_generate_steprejects as non-trimmable.This reproduces in both a normal draft speed smoke and a no-explicit-prompt-cache probe. Baseline generation without
--draft-modelworks.Related context: #872 tracks internal MTP support for Qwen/Nemotron models; this issue is specifically about external draft-model speculative decoding failing at runtime because the selected cache is not trimmable.
Environment
mlx-lm==0.31.3mlx==0.31.2mlx_lm.serverModels
Target model:
/Volumes/Models-toolkode/models/Qwen3.6-35B-A3B-UD-MLX-4bitQwen3_5MoeForConditionalGenerationqwen3_5_moeDraft model:
/Volumes/Models-toolkode/models/Qwen3.5-0.8B-8bitQwen3_5ForConditionalGenerationqwen3_5Repro
GET /v1/modelssucceeds. A chat completion request then fails during generation with:Trace tail:
Expected
Speculative decoding should either:
Actual
The server accepts startup/model requests, then the completion handler crashes during speculative generation because
ArraysCacheis not accepted as trimmable.Notes
This was found while benchmarking local ToolKode worker inference. Prefix-cache-only baseline works and the same target model without
--draft-modelgenerated normally at about 44 tok/s in our speed smoke.