You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Multiple system prompts break Qwen3.5-* models #15059
As part of investigating what could've caused Tarquinen/opencode-dynamic-context-pruning#407 I noticed that the tool was adding another system prompt, which caused for this model to break.
Despite being fixed in the plugin it makes sense to at least have it as a known issue, so this is mostly for documentation purposes and if there are other plugins that could incur such behavior for the qwen3.5 or other models.
Also maybe as a suggestion if there are multiple system prompts to be concatenated into a single one before dispatch to the model, similarly to how it is done here
Plugins
@tarquinen/opencode-dcp
OpenCode version
1.2.12
Steps to reproduce
Install @tarquinen/opencode-dcp with version <= 2.1.7
Start a session with any of the Qwen3.5 models
Send a message and expect to receive error this error
This patch normalizes the final system prompt into a single system message before provider request construction.
It is aimed at cases like the one reported here with Qwen, where multiple system prompt fragments can accumulate from provider/user/plugin input and strict chat templates appear to be sensitive to that final message shape.
Server:llama-server with --jinja (required by this model's official template)
Client: OpenCode (built from this PR branch)
Context:131072, tool calling enabled
With this patch, OpenCode correctly merges all system/developer messages into a single prompt before sending it to llama.cpp. The strict Jinja validation passes, and the model responds without breaking or hallucinating format.
🙏 Huge thanks to @zhangdw156 and the maintainers! This unblocks local Qwen3.x users who rely on strict chat templates.
❓ Quick question: Do you have an ETA for merging this into dev/main? Will it land in the next stable release (v1.14.x or v1.15.x)? I'm tracking it to update my global install as soon as it's available.
Happy to test edge cases (long context, multi-turn tool calling, vision+text, etc.) if needed. Keep up the amazing work! 🚀
Did this solution actually work for you? I remember trying to use that same chat template and adding it to my llamaCPP using the --chat-template-file argument, but it didn't work for me; my OpenCode kept giving me the same error.
You can check in the logs if the line srv params_from_: Chat format: peg-native is present. IMHO that indicates that a Jinja template is actually enabled and used.
complete llama-server log output for the above example request
It dont work for me. I need use the opencode patch or not work in the stable release
log error
clip_model_loader: has vision encoder
clip_ctx: CLIP using CUDA0 backend
load_hparams: Qwen-VL models require at minimum 1024 image tokens to function correctly on grounding tasks
load_hparams: if you encounter problems with accuracy, try adding --image-min-tokens 1024
load_hparams: more info: https://github.com/ggml-org/llama.cpp/issues/16842
load_hparams: projector: qwen3vl_merger
load_hparams: n_embd: 1152
load_hparams: n_head: 16
load_hparams: n_ff: 4304
load_hparams: n_layer: 27
load_hparams: ffn_op: gelu
load_hparams: projection_dim: 2048
--- vision hparams ---
load_hparams: image_size: 768
load_hparams: patch_size: 16
load_hparams: has_llava_proj: 0
load_hparams: minicpmv_version: 0
load_hparams: n_merge: 2
load_hparams: n_wa_pattern: 0
load_hparams: image_min_pixels: 8192
load_hparams: image_max_pixels: 4194304
load_hparams: model size: 860.98 MiB
load_hparams: metadata size: 0.12 MiB
warmup: warmup with image size = 1472 x 1472
alloc_compute_meta: CUDA0 compute buffer size = 248.10 MiB
alloc_compute_meta: CPU compute buffer size = 24.93 MiB
alloc_compute_meta: graph splits = 1, nodes = 823
warmup: flash attention is enabled
srv load_model: loaded multimodal model, 'C:\Users\User\.lmstudio\models\mudler\Qwen3.6-35B-A3B-uncensored-heretic-APEX-GGUF\mmproj.gguf'
srv load_model: initializing slots, n_slots = 1
common_speculative_is_compat: the target context does not support partial sequence removal
srv load_model: speculative decoding not supported by this context
slot load_model: id 0 | task -1 | new slot, n_ctx = 262144
srv load_model: prompt cache is enabled, size limit: 8192 MiB
srv load_model: use `--cache-ram 0` to disable the prompt cache
srv load_model: for more info see https://github.com/ggml-org/llama.cpp/pull/16391
srv init: init: idle slots will be saved to prompt cache and cleared upon starting a new task
init: chat template, example_format: '<|im_start|>system
You are a helpful assistant<|im_end|>
<|im_start|>user
Hello<|im_end|>
<|im_start|>assistant
Hi there<|im_end|>
<|im_start|>user
How are you?<|im_end|>
<|im_start|>assistant
<think>
'
srv init: init: chat template, thinking = 1
main: model loaded
main: server is listening on http://127.0.0.1:10000
main: starting the main loop...
srv update_slots: all slots are idle
srv operator (): got exception: {"error":{"code":500,"message":"\n------------\nWhile executing CallExpression at line 116, column 32 in source:\n...first %}↵ {{- raise_exception('System message must be at the beginnin...\n ^\nError: Jinja Exception: System message must be at the beginning.","type":"server_error"}}
srv log_server_r: done request: POST /v1/chat/completions 127.0.0.1 500
srv operator (): got exception: {"error":{"code":500,"message":"\n------------\nWhile executing CallExpression at line 116, column 32 in source:\n...first %}↵ {{- raise_exception('System message must be at the beginnin...\n ^\nError: Jinja Exception: System message must be at the beginning.","type":"server_error"}}
srv log_server_r: done request: POST /v1/chat/completions 127.0.0.1 500
srv operator (): got exception: {"error":{"code":500,"message":"\n------------\nWhile executing CallExpression at line 116, column 32 in source:\n...first %}↵ {{- raise_exception('System message must be at the beginnin...\n ^\nError: Jinja Exception: System message must be at the beginning.","type":"server_error"}}
srv log_server_r: done request: POST /v1/chat/completions 127.0.0.1 500
srv operator ():
got exception: {"error":{"code":500,"message":"\n------------\nWhile executing CallExpression at line 116, column 32 in source:\n...first %}↵ {{- raise_exception('System message must be at the beginnin...\n ^\nError: Jinja Exception: System message must be at the beginning.","type":"server_error"}}
srv log_server_r: done request: POST /v1/chat/completions 127.0.0.1 500
srv operator (): got exception: {"error":{"code":500,"message":"\n------------\nWhile executing CallExpression at line 116, column 32 in source:\n...first %}↵ {{- raise_exception('System message must be at the beginnin...\n ^\nError: Jinja Exception: System message must be at the beginning.","type":"server_error"}}
srv log_server_r: done request: POST /v1/chat/completions 127.0.0.1 500
still reproducible on opencode v1.14.39 against Qwen/Qwen3.6-27B and Qwen/Qwen3.6-35B-A3B on vLLM v0.21.0.
also fires with no plugins. opencode's title-generator path emits two role: "system" entries on the first call of any session (title-generator prompt + inherited delegation block), captured from the LiteLLM proxy spend log:
[0] system: "You are a title generator. You output ONLY a thread title..."
[1] system: "<task-notification><delegation-system> ## Async Delegation..."
[2] user: "Generate a title for this conversation:"
[3] user: "<actual user input>"
{"error":{"message":"System message must be at the beginning.","type":"BadRequestError","code":400}}
template clause: https://huggingface.co/Qwen/Qwen3.6-35B-A3B/raw/main/chat_template.jinja (search loop.first + raise_exception).
current build site is packages/opencode/src/session/llm/request.ts ~line 92:
Still reproducing on opencode 1.15.11 with a different plugin (opencode-goal-plugin@0.1.8), so dropping a fresh repro here in case it helps maintainers prioritize the coalescer fix.
Provider: @ai-sdk/openai-compatible -> self-hosted vLLM serving Qwen3.5-122B-A10B with the upstream chat template.
Run /goal <anything> in a session.
Request fails immediately with AI_APICallError and response body "System message must be at the beginning.".
Wire payload (from opencode's own debug log)
"messages": [
{ "role": "system", "content": "You are opencode, an interactive CLI tool ..." },
{ "role": "system", "content": "<goal_continuation>\n...\n<goal_objective>\ndraw a ball\n</goal_objective>\n..." },
{ "role": "user", "content": "draw a ball" }
]
Two role: "system" messages. The Qwen3.5 chat template raises on the second one (raise_exception('System message must be at the beginning.')), vLLM returns 400, opencode surfaces it as AI_APICallError.
Why this is opencode's bug (not just the plugin's)
The compiled binary shows opencode intends to coalesce plugin-added system blocks after experimental.chat.system.transform fires, but the guard is off-by-one — coalescing only runs when system.length > 2, so the common case of one plugin pushing one block (final length = 2) goes straight through to the provider as two separate role: "system" messages.
Decompiled snippet from the 1.15.11 binary:
consto=r[0];yield*A.plugin.trigger("experimental.chat.system.transform", ...,{system: r});if(r.length>2&&r[0]===o){// ← should be `> 1`constC=r.slice(1);r.length=0;r.push(o,C.join("\n"));}
Until this is fixed upstream, any plugin doing output.system.push(...) (the documented hook usage pattern) silently breaks strict-template backends.
Candidate fix already exists
cyrilmhansen#2 was merged to that fork on 2026-03-26 with exactly the right shape — collapse system fragments into a single newline-joined system message before provider request construction. It does not appear to have landed on anomalyco/opencodedev yet, and 1.15.11 still ships the broken coalescer. Any blockers to rebasing and merging it?
Plugin-side report filed in parallel
I've also filed william-ricchiuti/OpenCode-goal-plugin#1 asking the plugin to merge into system[0] defensively instead of push-ing, per the guidance requested in #23660. That fix is independent and gives users on strict backends a path forward without waiting on the upstream coalescer.
Bad Request: {"detail":"Invalid request: Invalid request: {\"detail\":{\"object\":\"error\",\"message\":\"System message must be at the beginning.\",\"type\":\"BadRequest\",\"param\":null,\"code\":400}}"}
Attempting to use locally hosted model via Nativ.app, mlx-community/Qwen3.6-35B-A3B-8bit, I get on the server side:
2026-08-12 11:41:13,147 - ERROR - Unexpected error in /chat/completions endpoint: System message must be at the beginning.
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.
Activity
marcobox commentedon Mar 8, 2026
I am noticing the same issue from my side as well. Here is my config:
Here is the an extract of what litellm sees:
I guess in my case superpowers is causing the issue?
DaGhostman commentedon Mar 9, 2026
yep, seems like it. I am not 100% sure what will be the correct approach, but I think that the plugin/mcp/etc adding those should be updated
cyrilmhansen commentedon Mar 26, 2026
I put together a candidate fix in my fork:
cyrilmhansen#2
This patch normalizes the final system prompt into a single system message before provider request construction.
It is aimed at cases like the one reported here with Qwen, where multiple system prompt fragments can accumulate from provider/user/plugin input and strict chat templates appear to be sensitive to that final message shape.
mingfang commentedon Apr 16, 2026
The problem is https://github.com/anomalyco/opencode/blob/dev/packages/opencode/src/session/llm.ts sends multiple system prompts and Qwen models(perhaps others) only supports one system prompt.
There's multiple open issues and PRs to fix this but don't understand what is the holdup.
Milor123 commentedon Apr 26, 2026
✅ Confirmed: The system prompt merge fix works perfectly (#23656
I just tested this PR locally and can confirm it completely resolves the
Jinja Exception: System message must be at the beginningerror.My setup:
HauhauCS/Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive(Q4_K_M)llama-serverwith--jinja(required by this model's official template)131072, tool calling enabledWith this patch, OpenCode correctly merges all system/developer messages into a single prompt before sending it to
llama.cpp. The strict Jinja validation passes, and the model responds without breaking or hallucinating format.🙏 Huge thanks to @zhangdw156 and the maintainers! This unblocks local Qwen3.x users who rely on strict chat templates.
❓ Quick question: Do you have an ETA for merging this into
dev/main? Will it land in the next stable release (v1.14.xorv1.15.x)? I'm tracking it to update my global install as soon as it's available.Happy to test edge cases (long context, multi-turn tool calling, vision+text, etc.) if needed. Keep up the amazing work! 🚀
countzero commentedon May 7, 2026
I can reproduce this behaviour with Qwen3.5 architecture models like Qwen3.6-27B-GGUF in OpenCode
v1.14.40.The error message from the
llama-serverbinary is:But this is a strict limitation from the official template (https://huggingface.co/Qwen/Qwen3.6-35B-A3B/blob/7da1103448ba36029c34ce1a9a741dfe93ee0c50/chat_template.jinja#L85) that can be fixed!
Workaround
Clone https://huggingface.co/froggeric/Qwen-Fixed-Chat-Templates and use
llama-serverwith the following options:--chat-template-file 'Qwen-Fixed-Chat-Templates\qwen3.6\chat_template.jinja'--chat-template-kwargs '{"preserve_thinking":true}'So maybe not a defect in OpenCode?
This works for me: https://github.com/countzero/windows_llama.cpp/blob/v1.30.0/presets/models_24GB_VRAM.ini#L179-L207
Milor123 commentedon May 7, 2026
Did this solution actually work for you? I remember trying to use that same chat template and adding it to my llamaCPP using the
--chat-template-file argument, but it didn't work for me; my OpenCode kept giving me the same error.countzero commentedon May 7, 2026
@Milor123 yes it works:
You can check in the logs if the line
srv params_from_: Chat format: peg-nativeis present. IMHO that indicates that a Jinja template is actually enabled and used.complete llama-server log output for the above example request
Milor123 commentedon May 7, 2026
It dont work for me. I need use the opencode patch or not work in the stable release
log error
input
.\llama-server.exe -m "C:\Users\User\.lmstudio\models\mudler\Qwen3.6-35B-A3B-uncensored-heretic-APEX-GGUF\Qwen3.6-35B-A3B-uncensored-heretic-APEX-I-Quality.gguf" --mmproj "C:\Users\User\.lmstudio\models\mudler\Qwen3.6-35B-A3B-uncensored-heretic-APEX-GGUF\mmproj.gguf" --host 127.0.0.1 --port 10000 -ngl 99 --n-cpu-moe 28 -c 262144 -t 8 -tb 14 -b 2048 -ub 1024 -np 1 -fa on -ctk turbo4 -ctv turbo3 --top-k 20 --repeat-penalty 1.0 --top-p 0.95 --min-p 0.0 -fit off --mlock --no-mmap -kvu --chat-template-kwargs '{"preserve_thinking":true}' --chat-template-file "S:\DownloadsWIN\chat_template.jinja" --jinjacountzero commentedon May 8, 2026
@Milor123
The error is coming from your chat template. Your
S:\DownloadsWIN\chat_template.jinjastill has the upstream guard at line 116:Interestingly there has been another update to the fixed template. I would now suggest you use the following:
Will also test that one once I find some time 😉
robertoberto commentedon May 23, 2026
still reproducible on opencode v1.14.39 against Qwen/Qwen3.6-27B and Qwen/Qwen3.6-35B-A3B on vLLM v0.21.0.
also fires with no plugins. opencode's title-generator path emits two role: "system" entries on the first call of any session (title-generator prompt + inherited delegation block), captured from the LiteLLM proxy spend log:
repro without opencode in the path:
current build site is packages/opencode/src/session/llm/request.ts ~line 92:
const messages = isOpenaiOauth || input.isWorkflow ? input.messages : [ ...system.map((x): ModelMessage => ({ role: "system", content: x })), ...input.messages, ]collapsing the array resolves it:
const messages = isOpenaiOauth || input.isWorkflow ? input.messages : [ ...(system.length > 0 ? [{ role: "system" as const, content: system.join("\n\n") }] : []), ...input.messages, ]#16981 generalizes this with a systemMessage: 'single' | 'multiple' capability and is currently waiting on @adamdotdevin review.
fank commentedon May 27, 2026
Still reproducing on opencode 1.15.11 with a different plugin (
opencode-goal-plugin@0.1.8), so dropping a fresh repro here in case it helps maintainers prioritize the coalescer fix.Repro
opencode-goal-plugin@0.1.8.@ai-sdk/openai-compatible-> self-hosted vLLM serving Qwen3.5-122B-A10B with the upstream chat template./goal <anything>in a session.AI_APICallErrorand response body"System message must be at the beginning.".Wire payload (from opencode's own debug log)
Two
role: "system"messages. The Qwen3.5 chat template raises on the second one (raise_exception('System message must be at the beginning.')), vLLM returns 400, opencode surfaces it asAI_APICallError.Why this is opencode's bug (not just the plugin's)
The compiled binary shows opencode intends to coalesce plugin-added system blocks after
experimental.chat.system.transformfires, but the guard is off-by-one — coalescing only runs whensystem.length > 2, so the common case of one plugin pushing one block (final length = 2) goes straight through to the provider as two separaterole: "system"messages.Decompiled snippet from the 1.15.11 binary:
Until this is fixed upstream, any plugin doing
output.system.push(...)(the documented hook usage pattern) silently breaks strict-template backends.Candidate fix already exists
cyrilmhansen#2 was merged to that fork on 2026-03-26 with exactly the right shape — collapse
systemfragments into a single newline-joined system message before provider request construction. It does not appear to have landed onanomalyco/opencodedevyet, and 1.15.11 still ships the broken coalescer. Any blockers to rebasing and merging it?Plugin-side report filed in parallel
I've also filed william-ricchiuti/OpenCode-goal-plugin#1 asking the plugin to merge into
system[0]defensively instead ofpush-ing, per the guidance requested in #23660. That fix is independent and gives users on strict backends a path forward without waiting on the upstream coalescer.Environment
@ai-sdk/openai-compatibleQuick A/B validation of the plugin-side workaround against my reproducer (opencode 1.15.11 + goal-plugin 0.1.8 + Qwen3.5-122B vLLM):
output.system.push(...)(current goal-plugin behavior)"System message must be at the beginning."output.system[0](patched locally in the plugin file)This confirms both:
output.system.pushpattern that the hook signature suggests.Tracking the plugin-side fix at william-ricchiuti/OpenCode-goal-plugin#1 to unblock users now while the upstream coalescer fix lands.
OneOfOne commentedon Jun 3, 2026
I'm running into this as well, not sure how to debug.
SurealCereal commentedon Aug 6, 2026
I see this issue with some models, now with
bottlecapai/ThinkingCap-Qwen3.6-27B-NVFP4on vLLM.zcutlip commentedon Aug 12, 2026
on
chutes/Qwen/Qwen3.6-27B-TEE, I'm getting:Attempting to use locally hosted model via Nativ.app,
mlx-community/Qwen3.6-35B-A3B-8bit, I get on the server side:Nativ.app more info: https://github.com/Blaizzy/nativ
thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊