Skip to content

/model switch notices and error banners are stored as system-role messages and replayed mid-conversation, breaking strict chat templates #13026

Description

@garymarkowitz-source

Before submitting your bug report

Relevant environment info

- OS: Windows 11
  - Continue version: CLI (cn) v1.5.47, interactive TUI mode
  - IDE version: n/a (CLI)
  - Model: qwen/qwen3.6-35b-a3b (Q6_K, lmstudio-community) via LM Studio; second provider
  OpenRouter
  - config: two providers in one config — LM Studio (local, OpenAI-compatible) + OpenRouter

Description

Summary

When switching models mid-session with /model, cn records its UI notices — Switched to model: <name> and even transport errors like Error: Connection error. — as system-role messages in the conversation history, and replays them to the server inside the messages array of every subsequent request.

Many chat templates hard-reject a system message anywhere but position 0. Qwen3's template contains:

{%- if message.role == "system" %}
    {%- if not loop.first %}
        {{- raise_exception('System message must be at the beginning.') }}
    {%- endif %}

so the first prompt after switching onto such a model fails with an HTTP 400 from LM Studio:

Unable to generate parser for this template. Automatic parser generation failed:
...
Error: Jinja Exception: System message must be at the beginning.

Every subsequent prompt to that model fails the same way for the rest of the session. /clear does not recover — the switch notice is re-injected (tested clearing both before and after the switch). Only restarting cn recovers.

The failure is easy to misdiagnose: whether it triggers depends on the destination model's template strictness (Qwen3.x Max via OpenRouter tolerates mid-history system messages; local Qwen3.6 35b does not), so it presents as "switching to my local provider breaks the session."

Related issue

#12963 reports the same defect class (a system-role message injected mid-conversation, rejected with the identical "System message must be at the beginning" error) with a different trigger — tool-request cancellation in the IDE extension. This report covers the CLI's /model switch and error-banner paths, which that issue's proposed fix (scoped to the cancellation handler) would not address.

Source pointer

extensions/cli/src/ui/hooks/useModelSelector.ts emits the switch notice as:

onMessage({
  role: "system",
  content: `Switched to model: ${modelInfo?.name}`,
  messageType: "system",
});

messageType: "system" suggests this was intended as a UI-only display classification, but role: "system" places it in the conversation history that gets replayed to the provider. The same pattern appears in useConfigSelector.ts, useChat.ts, and compaction.ts (likely the source of the replayed Error: Connection error. banners).

Evidence: captured request body

LM Studio's DEBUG log shows the request cn sent after several switches (trimmed; note the system entries between turns):

{
  "model": "qwen/qwen3.6-35b-a3b",
  "messages": [
    { "role": "system", "content": "You are an agent in the Continue CLI. ..." },
    { "role": "user", "content": "what is the solution name" },
    { "role": "assistant", "content": "", "tool_calls": [ "..." ] },
    { "role": "tool", "content": "...", "tool_call_id": "..." },
    { "role": "assistant", "content": "The solution file is ..." },
    { "role": "system", "content": "Switched to model: Qwen3.7 Max" },
    { "role": "user", "content": "what's the airspeed velocity of an unladen swallow?" },
    { "role": "assistant", "content": "African or European?" },
    { "role": "user", "content": "I don't know" },
    { "role": "assistant", "content": "You have been cast into the Gorge of Eternal Peril ..." },
    { "role": "system", "content": "Switched to model: Qwen3 Coder 30b" },
    { "role": "user", "content": "What's the airspeed velocity of an unladen swallow?" },
    { "role": "system", "content": "Error: Connection error." },
    { "role": "user", "content": "what is today's date" },
    { "role": "system", "content": "Error: Connection error." },
    { "role": "system", "content": "Switched to model: Qwen3.7 Max" },
    { "role": "user", "content": "what is today's date" },
    { "role": "assistant", "content": "Today is **July 25, 2026**." },
    { "role": "system", "content": "Switched to model: Qwen3.6 35b A3b" },
    { "role": "user", "content": "what is today's date" }
  ],
  "stream": true,
  "tools": [ "..." ]
}

A fresh session against the same model, config, rules, and tools works perfectly (tool calls included) — the defect is purely the mid-history system entries.

Suggested fix

Keep the display classification (messageType: "system") but stop assigning role: "system" to UI events — store switch notices and error banners as UI-only/display events excluded from the replayed history (or, if they should be model-visible, emit them as user-role context). A system message after position 0 is rejected outright by a growing set of chat templates, so any replayed system-role event breaks those models for the remainder of the session.

Workaround (for anyone else hitting this)

Either restart cn after switching onto a strict-template model, or edit the model's Jinja template in LM Studio so the non-first system branch renders '<|im_start|>system\n' + content + '<|im_end|>\n' instead of raising. Verified working — but it's per-model, local, and lost if the model is re-downloaded.

To reproduce

Steps to reproduce

  1. Config with two chat models, at least one served by LM Studio with a strict template (any current Qwen3 works).
  2. Start cn, send any prompt.
  3. /model → switch to the strict-template model.
  4. Send any prompt.

Expected: the new model answers.
Actual: 400 as above; model unusable until cn is restarted.

Log output

2026-07-25 18:03:46 [DEBUG]
   Received request: POST to /v1/chat/completions with body  {
    "model": "qwen/qwen3.6-35b-a3b",
    "messages": [
      ...
      { "role": "system", "content": "Switched to model: Qwen3.7 Max" },
      ...
      { "role": "system", "content": "Error: Connection error." },
      ...
      { "role": "system", "content": "Switched to model: Qwen3.6 35b A3b" },
      { "role": "user", "content": "what is today's date" }
    ],
    ...
  }
  2026-07-25 18:03:46  [INFO]
   [qwen/qwen3.6-35b-a3b] Running chat completion on conversation with 22 messages.
  2026-07-25 18:03:46  [INFO]
   [qwen/qwen3.6-35b-a3b] Streaming response...
  2026-07-25 18:03:47 [DEBUG]
   466.45.572.562 W srv    operator(): got exception: {"error":{"code":400,"message":"Unable
  to generate parser for this template. Automatic parser generation failed:
  \n------------\nWhile executing CallExpression at line 110, column 34 in source:\n...rst
  %}↵              {{- raise_exception('System message must be at the↵  beginn...\n
                                  ^\nError: Jinja Exception: System message must be at the\n
   beginn...","type":"invalid_request_error"}}
  2026-07-25 18:03:47 [ERROR]
   [qwen/qwen3.6-35b-a3b] Engine protocol predict request returned 400:
  {"error":{"code":400,"message":"Unable to generate parser for this template. Automatic
  parser generation failed: ...Error: Jinja Exception: System message must be at the\n
  beginning.","type":"invalid_request_error"}}. Error Data: n/a, Additional Data: n/a

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