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
tools defined. The model correctly emits a <tool_call> and finish_reason = tool_calls.
- Send a follow-up request with the same conversation plus the
assistant (tool_calls) and tool (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.
Summary
On multi-turn tool-call conversations,
flm servefails withNo valid checkpoint to restorefollowed byMax length reached, stopping prefilling.... The model never produces a final answer — the second turn (after the tool result is returned) is rejected.Environment
qwen3.6-moe:35b-a3b(NPU2)POST /v1/chat/completionsReproduction
flm serve qwen3.6-moe:35b-a3b --ctx-len 262144 --pmode performance.toolsdefined. The model correctly emits a<tool_call>andfinish_reason = tool_calls.assistant(tool_calls) andtool(result) messages.Log
The server returns:
{"error":{"message":"Max length reached!","type":"model_error","code":400}}Root cause
Qwen3_6_MOE::insert()callsqwen3_6_moe_npu::restore()whenmeta_info.restore_allowed == true. On the second turn,restore()returns a negative value (no valid checkpoint), but the method then unconditionally assigns:checkpoint_hisstill holds the previous turn's token history (~16k tokens).total_tokensis now desynchronized from the actual KV-cache size._shared_insert()then checks:Because
total_tokenswas never reset on restore failure, the check fires even though the actual context is well belowMAX_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, insideQwen3_6_MOE::insert():1. Handle
restore()failure explicitly — reset state instead of assigning a negativerestore_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_tokenswithtoken_historyafter_shared_insert()— guarantees the counter reflects the real KV-cache size regardless of what happened inrestore()/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:
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 serveonqwen3.6-moewithout this fix. The bug affects all multi-turn tool interactions, not just a specific client.