Skip to content

No valid checkpoint to restore + Max length reached on multi-turn tool calls (Qwen3.6-MoE, NPU) #744

Description

@winaes13-ux

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

  1. Start flm serve qwen3.6-moe:35b-a3b --ctx-len 262144 --pmode performance.
  2. Send a request with tools defined. The model correctly emits a <tool_call> and finish_reason = tool_calls.
  3. Send a follow-up request with the same conversation plus the assistant (tool_calls) and tool (result) messages.
  4. 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.


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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions