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
When running unsloth/Qwen3.5-27B-GGUF:Q3_K_S model I'm getting this error:
srv operator(): got exception: {"error":{"code":500,"message":"\n------------\nWhile executing CallExpression at line 85, 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 85, 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
Could you give more information? Are you trying to use QWEN as your main model or historian, dreamer etc? Also what error you're getting? Your configs and logs would be also valuable.
bunx @cortexkit/opencode-magic-context@latest doctor
output also
I'm think is something related to --jinja template thing because of this message: Error: Jinja Exception: System message must be at the beginning.","type":"server_error"}}
Thanks for the detailed repro — I traced this and it's a real interaction, but not strictly a magic-context bug. Here's what I found:
Root cause
Qwen3.5's Jinja chat template enforces a strict "exactly one system message at position 0" rule. When llama-server is run with --jinja, it renders the actual template, which walks the messages list and raises the exception you're seeing the moment it encounters a second role: "system" message.
The problem is that OpenCode sends two system messages to the provider when any plugin modifies the system prompt:
system[0]: the agent base prompt (kept separate so Anthropic prompt caching stays stable across plugin changes)
system[1]: plugin-injected content rejoined into one message
// rejoin to maintain 2-part structure for caching if header unchangedif(system.length>2&&system[0]===header){constrest=system.slice(1)system.length=0system.push(header,rest.join("\n"))}// ...constmessages=[
...system.map((x): ModelMessage=>({role: "system",content: x})),
...input.messages,]
Magic-context uses the supported experimental.chat.system.transform hook to inject per-agent guidance, project docs, user profile, and pinned key files. Each push produces extra entries in system[], which OpenCode then rejoins down to exactly 2 messages. Qwen3.5's template accepts the first but raises on the second.
So without magic-context, OpenCode sends 1 system message and Qwen is happy. With magic-context (or any plugin that modifies system prompt), OpenCode sends 2 and Qwen rejects.
Quick confirmation
You can verify this is the exact cause by temporarily disabling the magic-context plugin in opencode.json (remove the plugin entry or comment it out) and running the same historian call. If it succeeds with 1 system message and fails with 2, that confirms the chain.
Workarounds (pick one)
1. Drop --jinja from your llama-server command (easiest)
Without --jinja, llama.cpp uses its built-in ChatML rendering, which is permissive about system message placement. You'll lose Qwen3.5-specific template features but the historian should work.
2. Override the chat template
Pass --chat-template chatml (or a custom permissive template) to llama-server. This replaces Qwen's strict template with one that accepts multiple system messages.
3. Use a different local model for historian
Any model whose template doesn't enforce strict single-system placement will work. Most GGUF models with ChatML-style templates are fine.
4. Proxy your llama-server through something that merges system messages
A thin proxy that joins consecutive system messages into one before forwarding to llama-server would work cleanly, but it's more setup than the other options.
Longer-term fix
The real fix belongs upstream in OpenCode: detect providers/templates that require a single system message and consolidate before sending. I'll note this for a potential OpenCode issue, but given that it affects the prompt-caching structure for Anthropic, it needs careful handling.
I can also consider adding a config flag to magic-context to suppress the system-prompt injection entirely for users on strict-template local models — that would get you magic-context's core functionality (historian, dreamer, ctx tools) without the system prompt side. Let me know if that would be useful.
For now I'd recommend workaround #1 (drop --jinja) as the quickest unblocker. Please confirm whether that gets you running.
Activity
ualtinok commentedon Apr 10, 2026
Could you give more information? Are you trying to use QWEN as your main model or historian, dreamer etc? Also what error you're getting? Your configs and logs would be also valuable.
bunx @cortexkit/opencode-magic-context@latest doctoroutput also
julitroalves commentedon Apr 10, 2026
julitroalves commentedon Apr 10, 2026
magic-context.jsonc:
julitroalves commentedon Apr 10, 2026
Yeah, QWEN is my main local model. I'm using it as a historian.
julitroalves commentedon Apr 10, 2026
I'm think is something related to --jinja template thing because of this message:
Error: Jinja Exception: System message must be at the beginning.","type":"server_error"}}ualtinok commentedon Apr 11, 2026
(Answer from MagicContext Agent)
Thanks for the detailed repro — I traced this and it's a real interaction, but not strictly a magic-context bug. Here's what I found:
Root cause
Qwen3.5's Jinja chat template enforces a strict "exactly one system message at position 0" rule. When
llama-serveris run with--jinja, it renders the actual template, which walks the messages list and raises the exception you're seeing the moment it encounters a secondrole: "system"message.The problem is that OpenCode sends two system messages to the provider when any plugin modifies the system prompt:
system[0]: the agent base prompt (kept separate so Anthropic prompt caching stays stable across plugin changes)system[1]: plugin-injected content rejoined into one messageThat logic lives in
opencode/packages/opencode/src/session/llm.ts:122-163:Magic-context uses the supported
experimental.chat.system.transformhook to inject per-agent guidance, project docs, user profile, and pinned key files. Each push produces extra entries insystem[], which OpenCode then rejoins down to exactly 2 messages. Qwen3.5's template accepts the first but raises on the second.So without magic-context, OpenCode sends 1 system message and Qwen is happy. With magic-context (or any plugin that modifies system prompt), OpenCode sends 2 and Qwen rejects.
Quick confirmation
You can verify this is the exact cause by temporarily disabling the magic-context plugin in
opencode.json(remove the plugin entry or comment it out) and running the same historian call. If it succeeds with 1 system message and fails with 2, that confirms the chain.Workarounds (pick one)
1. Drop
--jinjafrom your llama-server command (easiest)Without
--jinja, llama.cpp uses its built-in ChatML rendering, which is permissive about system message placement. You'll lose Qwen3.5-specific template features but the historian should work.2. Override the chat template
Pass
--chat-template chatml(or a custom permissive template) to llama-server. This replaces Qwen's strict template with one that accepts multiple system messages.3. Use a different local model for historian
Any model whose template doesn't enforce strict single-system placement will work. Most GGUF models with ChatML-style templates are fine.
4. Proxy your llama-server through something that merges system messages
A thin proxy that joins consecutive system messages into one before forwarding to llama-server would work cleanly, but it's more setup than the other options.
Longer-term fix
The real fix belongs upstream in OpenCode: detect providers/templates that require a single system message and consolidate before sending. I'll note this for a potential OpenCode issue, but given that it affects the prompt-caching structure for Anthropic, it needs careful handling.
I can also consider adding a config flag to magic-context to suppress the system-prompt injection entirely for users on strict-template local models — that would get you magic-context's core functionality (historian, dreamer, ctx tools) without the system prompt side. Let me know if that would be useful.
For now I'd recommend workaround #1 (drop
--jinja) as the quickest unblocker. Please confirm whether that gets you running.fix: close remaining council audit findings (#5, #6, #9, #10)
fix(pi-plugin): close council audit findings (#1, #3, #4, #5, #7) + P…
fix(pi): #5 stale ctx_reduce drop uses composite (owner,callId) identity
fix(pi): round-8 cluster — marker isolation, contention fallback, sig…
fix: 2-bug council findings — memory config bypass, session identity,…
fix: full-codebase audit findings (data-integrity, fail-closed, secur…
fix(audit): dreamer lease atomicity, auto-update config safety, dream…
fix(audit): full-codebase council round — 9 real fixes + false-positi…
13 remaining items
fix(pi): round-8 cluster — marker isolation, contention fallback, sig…
fix: 2-bug council findings — memory config bypass, session identity,…
fix: full-codebase audit findings (data-integrity, fail-closed, secur…
fix(audit): dreamer lease atomicity, auto-update config safety, dream…
fix(audit): full-codebase council round — 9 real fixes + false-positi…
fix(cache): resolve transform/cache-stability council audit (5 real b…
fix: resolve remaining v0.22.5 audit findings (cortexkit#5, cortexkit#6…
fix(ctx-reduce): round-2 audit batch 2 — Pi config bleed, sqlite proj…
Fix re-audit Councils A+B findings (FK scope, identity divergence, le…
Dashboard: fix jsonc UTF-8/string corruption (cortexkit#5) and guard …
fix: address high-priority audit findings (cortexkit#13, cortexkit#20-…
fix: close 7 audit findings from v0.2.0 council review
fix: cap execute_threshold at 80% and invalidate compartment cache on…
fix(plugin): 8 audit fixes — JSON safety, dream scheduling, smart not…
fix(profiles): hunt #5 — profile-resolved historian/dreamer models no…