No valid checkpoint to restore + Max length reached on multi-turn tool calls (Qwen3.6-MoE, NPU)
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 1.9k
- Forks
- 152
- Avg merge
- 4h 14m
- Merged PRs (30d)
- 11
Description
Summary
On multi-turn tool-call conversations, flm serve fails with No valid checkpoint to restore followed by Max length reached, stopping prefilling.... The model never produces a final answer — the second turn (after the tool result is returned) is rejected.
Environment
| FLM version | 1.0.5 |
| Model | qwen3.6-moe:35b-a3b (NPU2) |
| NPU firmware | 1.1.2.64 |
| Platform | Linux, AMD Ryzen AI 9 HX 470 (Strix Halo) |
| Client | OpenCode 1.18.31 (OpenAI-compatible API) |
| Endpoint | POST /v1/chat/completions |
Reproduction
- Start
flm serve qwen3.6-moe:35b-a3b --ctx-len 262144 --pmode performance. - Send a request with
toolsdefined. The model correctly emits a<tool_call>andfinish_reason = tool_calls. - Send a follow-up request with the same conversation plus the
assistant(tool_calls) andtool(result) messages. - FLM fails — see log below.
Log
[FLM] Use cached prompt!
[FLM] Matched 2 out of 4 messages (2 new to prefill).
[FLM] Start prefill...
[FLM] Total images: 0
[FLM] No valid checkpoint to restore
[WARNING] Max length reached, stopping prefilling...
[FLM] Creating checkpoint at context length 0
[🔵 ] NPU Lock Released!
The server returns:
{"error":{"message":"Max length reached!","type":"model_error","code":400}}
Root cause
Qwen3_6_MOE::insert() calls qwen3_6_moe_npu::restore() when meta_info.restore_allowed == true. On the second turn, restore() returns a negative value (no valid checkpoint), but the method then unconditionally assigns:
this->total_tokens = restore_idx; // becomes -1
this->token_history = checkpoint_his;
checkpoint_his still holds the previous turn's token history (~16k tokens). total_tokens is now desynchronized from the actual KV-cache size. _shared_insert() then checks:
if (this->total_tokens + tokens.size() >= this->MAX_L) {
header_print("WARNING", "Max length reached, stopping prefilling...");
return false;
}
Because total_tokens was never reset on restore failure, the check fires even though the actual context is well below MAX_L (32 768). clear_context() is never called, so the KV cache is left in an inconsistent state.
Proposed fix
In common/AutoModel/modeling_qwen3_6_moe.cpp, inside Qwen3_6_MOE::insert():
1. Handle restore() failure explicitly — reset state instead of assigning a negative restore_idx:
if (meta_info.restore_allowed) {
restore_idx = qwen3_6_moe_engine->restore();
- this->total_tokens = restore_idx;
- this->token_history = checkpoint_his;
+ if (restore_idx < 0) {
+ this->lm_engine->clear_context();
+ this->total_tokens = 0;
+ this->token_history.clear();
+ this->checkpoint_his.clear();
+ } else {
+ this->total_tokens = restore_idx;
+ this->token_history = checkpoint_his;
+ }
}
2. Re-sync total_tokens with token_history after _shared_insert() — guarantees the counter reflects the real KV-cache size regardless of what happened in restore() / clear_context() / _shared_insert():
bool success = has_images
? this->_shared_insert(meta_info, tokens, is_cancelled, &image_payload, last_image_token_index)
: this->_shared_insert(meta_info, tokens, is_cancelled, nullptr);
+ if (this->total_tokens != (int)this->token_history.size()) {
+ this->total_tokens = this->token_history.size();
+ }
+
checkpoint_his = token_history;
int checkpoint_idx = qwen3_6_moe_engine->checkpoint();
return success;
}
Verification
After applying the patch and rebuilding FLM, the same multi-turn tool call now completes successfully. Full log:
[FLM] Use cached prompt!
[FLM] Matched 2 out of 4 messages (2 new to prefill).
[FLM] Start prefill...
[FLM] Total images: 0
[FLM] No valid checkpoint to restore
[FLM] Prefill chunk 1/5 with 4096 tokens
[FLM] Prefill chunk 2/5 with 4096 tokens
[FLM] Prefill chunk 3/5 with 4096 tokens
[FLM] Prefill chunk 4/5 with 4096 tokens
[FLM] Prefill chunk 5/5 with 303 tokens
[FLM] Creating checkpoint at context length 16687
[FLM] Start generating...
[FLM] Model RAW Output:
Вот список проиндексированных проектов:
... (correct answer)
The model now returns the expected final answer instead of a 400 error.
Impact
Tool-calling agents (OpenCode, Claude Code, Cline, and any OpenAI-compatible client that performs multi-step tool calls) are unusable with flm serve on qwen3.6-moe without this fix. The bug affects all multi-turn tool interactions, not just a specific client.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in common/AutoModel/modeling_qwen3_6_moe.cpp at Qwen3_6_MOE::insert(), then trace restore(), _shared_insert(), clear_context(), and the token-history state. Rebuild FLM and repeat the documented multi-turn tool-call request; done means the second turn completes with a final answer instead of the max-length 400 error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, linux
- Domain
- ai-infra-agents
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100