Ministral 3 support: error "Only user, assistant and tool roles are supported, got system" #3287

Open
opened 2026-02-16 17:39:31 -05:00 by yindo · 13 comments
Owner

Originally created by @espadrine on GitHub (Dec 3, 2025).

Originally assigned to: @rekram1-node on GitHub.

Description

I wish to use opencode with Ministral 3 14B which was recently released. However, opencode raises an error immediately:

Only user, assistant and tool roles are supported, got system

To understand it better, here is first my setup, and how to reproduce the error, which I expect will occur even for the Ministral 3 3B, as we will see later.

I run the server using weights downloaded from unsloth:

huggingface-cli download unsloth/Ministral-3-14B-Reasoning-2512-GGUF Ministral-3-14B-Reasoning-2512-Q5_K_M.gguf
docker run --gpus all -v /data/ml/models/gguf:/models -p 11432:8080 ghcr.io/ggml-org/llama.cpp:full-cuda -s --host 0.0.0.0 -m "/models/Ministral-3-14B-Reasoning-2512-Q5_K_M.gguf" --ctx-size 0 -ub 2048 -b 2048 --n-gpu-layers 99 -fa on -c 65536

I then run opencode with the following configuration, as per the documentation:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "llama.cpp": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "llama-server (local)",
      "options": {
        "baseURL": "http://127.0.0.1:11432/v1"
      },
      "models": {
        "ministral-3-14b": {
          "name": "Ministral 3 14B (local)"
        }
      }
    }
  }
}

Then I request anything, and get the error pasted at the top.

The reason I get this error is that llama.cpp relies on the Jinja template inset in the GGUF, which it copied from the original Ministral repository. It loops over loop_messages, expecting not to have system messages. It only expects a system message on the first message.

However, that clearly is not the case with opencode, which can seemingly have up to 3 system messages: https://github.com/sst/opencode/blob/ee4437ff32fc2acbd2220060fc980a096730bcee/packages/opencode/src/session/prompt.ts#L651-L660

For opencode to support Ministral, it needs a single system prompt with the current Jinja template, concatenating them.

OpenCode version

1.0.129

Steps to reproduce

I run the server using weights downloaded from unsloth:

huggingface-cli download unsloth/Ministral-3-14B-Reasoning-2512-GGUF Ministral-3-14B-Reasoning-2512-Q5_K_M.gguf
docker run --gpus all -v /data/ml/models/gguf:/models -p 11432:8080 ghcr.io/ggml-org/llama.cpp:full-cuda -s --host 0.0.0.0 -m "/models/Ministral-3-14B-Reasoning-2512-Q5_K_M.gguf" --ctx-size 0 -ub 2048 -b 2048 --n-gpu-layers 99 -fa on -c 65536

I then run opencode with the following configuration, as per the documentation:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "llama.cpp": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "llama-server (local)",
      "options": {
        "baseURL": "http://127.0.0.1:11432/v1"
      },
      "models": {
        "ministral-3-14b": {
          "name": "Ministral 3 14B (local)"
        }
      }
    }
  }
}

Then I enter a message, and I see the error:

Only user, assistant and tool roles are supported, got system

Screenshot and/or share link

Image

Operating System

Ubuntu 24.04.2 LTS

Terminal

Alacritty

Originally created by @espadrine on GitHub (Dec 3, 2025). Originally assigned to: @rekram1-node on GitHub. ### Description I wish to use opencode with [Ministral 3 14B](https://mistral.ai/news/mistral-3) which was recently released. However, opencode raises an error immediately: > Only user, assistant and tool roles are supported, got system To understand it better, here is first my setup, and how to reproduce the error, which I expect will occur even for the Ministral 3 3B, as we will see later. I run the server using [weights downloaded from unsloth](https://huggingface.co/unsloth/Ministral-3-14B-Reasoning-2512-GGUF): ```bash huggingface-cli download unsloth/Ministral-3-14B-Reasoning-2512-GGUF Ministral-3-14B-Reasoning-2512-Q5_K_M.gguf docker run --gpus all -v /data/ml/models/gguf:/models -p 11432:8080 ghcr.io/ggml-org/llama.cpp:full-cuda -s --host 0.0.0.0 -m "/models/Ministral-3-14B-Reasoning-2512-Q5_K_M.gguf" --ctx-size 0 -ub 2048 -b 2048 --n-gpu-layers 99 -fa on -c 65536 ``` I then run opencode with the following configuration, as per the [documentation](https://opencode.ai/docs/providers/#llamacpp): ```json { "$schema": "https://opencode.ai/config.json", "provider": { "llama.cpp": { "npm": "@ai-sdk/openai-compatible", "name": "llama-server (local)", "options": { "baseURL": "http://127.0.0.1:11432/v1" }, "models": { "ministral-3-14b": { "name": "Ministral 3 14B (local)" } } } } } ``` Then I request anything, and get the error pasted at the top. The reason I get this error is that llama.cpp relies on the Jinja template inset in the GGUF, which it copied from [the original Ministral repository](https://huggingface.co/mistralai/Ministral-3-14B-Reasoning-2512/blob/main/chat_template.jinja). It loops over loop_messages, expecting not to have system messages. It only expects a system message on the first message. However, that clearly is not the case with opencode, which can seemingly have up to 3 system messages: https://github.com/sst/opencode/blob/ee4437ff32fc2acbd2220060fc980a096730bcee/packages/opencode/src/session/prompt.ts#L651-L660 For opencode to support Ministral, it needs a single system prompt with the current Jinja template, concatenating them. ### OpenCode version 1.0.129 ### Steps to reproduce I run the server using [weights downloaded from unsloth](https://huggingface.co/unsloth/Ministral-3-14B-Reasoning-2512-GGUF): ```bash huggingface-cli download unsloth/Ministral-3-14B-Reasoning-2512-GGUF Ministral-3-14B-Reasoning-2512-Q5_K_M.gguf docker run --gpus all -v /data/ml/models/gguf:/models -p 11432:8080 ghcr.io/ggml-org/llama.cpp:full-cuda -s --host 0.0.0.0 -m "/models/Ministral-3-14B-Reasoning-2512-Q5_K_M.gguf" --ctx-size 0 -ub 2048 -b 2048 --n-gpu-layers 99 -fa on -c 65536 ``` I then run opencode with the following configuration, as per the [documentation](https://opencode.ai/docs/providers/#llamacpp): ```json { "$schema": "https://opencode.ai/config.json", "provider": { "llama.cpp": { "npm": "@ai-sdk/openai-compatible", "name": "llama-server (local)", "options": { "baseURL": "http://127.0.0.1:11432/v1" }, "models": { "ministral-3-14b": { "name": "Ministral 3 14B (local)" } } } } } ``` Then I enter a message, and I see the error: > Only user, assistant and tool roles are supported, got system ### Screenshot and/or share link <img width="1077" height="419" alt="Image" src="https://github.com/user-attachments/assets/2c8b389a-10d4-4717-b922-392daccf460e" /> ### Operating System Ubuntu 24.04.2 LTS ### Terminal Alacritty
yindo added the bug label 2026-02-16 17:39:31 -05:00
Author
Owner

@github-actions[bot] commented on GitHub (Dec 3, 2025):

This issue might be a duplicate of existing issues. Please check:

  • #2272: TemplateError about conversation roles needing to alternate - similar root cause where consecutive system messages are sent to servers with Jinja templates
  • #1890: OpenCode sends tools and Jinja tool template to llama.cpp causing 500 errors - addresses llama.cpp compatibility issues with multiple system messages
  • #4107: Databricks Gemini 2.5 Pro error "Gemini models only support one system prompt" - indicates the codebase sends multiple system prompts, which some providers reject

All three issues stem from the same underlying problem: OpenCode generates up to 3 system messages (as referenced in prompt.ts:651-660), but many model providers and Jinja templates expect only a single system message, especially at the start of conversation.

Feel free to ignore if your case differs, but these likely share the same root cause and solution.

@github-actions[bot] commented on GitHub (Dec 3, 2025): This issue might be a duplicate of existing issues. Please check: - #2272: TemplateError about conversation roles needing to alternate - similar root cause where consecutive system messages are sent to servers with Jinja templates - #1890: OpenCode sends tools and Jinja tool template to llama.cpp causing 500 errors - addresses llama.cpp compatibility issues with multiple system messages - #4107: Databricks Gemini 2.5 Pro error "Gemini models only support one system prompt" - indicates the codebase sends multiple system prompts, which some providers reject All three issues stem from the same underlying problem: OpenCode generates up to 3 system messages (as referenced in prompt.ts:651-660), but many model providers and Jinja templates expect only a single system message, especially at the start of conversation. Feel free to ignore if your case differs, but these likely share the same root cause and solution.
Author
Owner

@arkamar commented on GitHub (Dec 5, 2025):

This one might be related to https://github.com/ggml-org/llama.cpp/issues/17700 and https://github.com/ggml-org/llama.cpp/pull/17713.

EDIT: actually I am no longer able to reproduce this:

The error message changes to

After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results

when I apply https://github.com/ggml-org/llama.cpp/pull/17713.

@arkamar commented on GitHub (Dec 5, 2025): This one might be related to https://github.com/ggml-org/llama.cpp/issues/17700 and https://github.com/ggml-org/llama.cpp/pull/17713. **EDIT**: actually I am no longer able to reproduce this: > The error message changes to > ``` > After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results > ``` > when I apply https://github.com/ggml-org/llama.cpp/pull/17713.
Author
Owner

@rekram1-node commented on GitHub (Dec 7, 2025):

I think this is an issue on llama.cpp side right

@rekram1-node commented on GitHub (Dec 7, 2025): I think this is an issue on llama.cpp side right
Author
Owner

@arkamar commented on GitHub (Dec 7, 2025):

Well, I don't know. The problem is in default ministral jinja template https://huggingface.co/mistralai/Ministral-3-3B-Reasoning-2512/blob/f8aa436a36b5bf3c59e3c2e8b19368de9cf69914/chat_template.jinja, which basically requires alternating roles in messages. Therefore:

{
    ...
    "messages": [
        {
            "role":"system",
            "content":"You are"
        },
        {
            "role":"system",
            "content":"Here is"
        },
        {
            "role":"user",
            "content":"test"
        }
    ],
    ...
}

and

{
    ...
    "messages": [
        {
            "role":"system",
            "content":"You are"
        },
        {
            "role":"user",
            "content":"test 1"
        }
        {
            "role":"user",
            "content":"test 2"
        }
    ],
    ...
}

are both problematic. The first snippet is what opencode basically sends by default and the template throws an error: Only user, assistant and tool roles are supported, got system. at row 124, column 111... for it. The issue seems to be related to https://github.com/sst/opencode/issues/2272.

@arkamar commented on GitHub (Dec 7, 2025): Well, I don't know. The problem is in default ministral jinja template https://huggingface.co/mistralai/Ministral-3-3B-Reasoning-2512/blob/f8aa436a36b5bf3c59e3c2e8b19368de9cf69914/chat_template.jinja, which basically requires alternating roles in messages. Therefore: ```json { ... "messages": [ { "role":"system", "content":"You are" }, { "role":"system", "content":"Here is" }, { "role":"user", "content":"test" } ], ... } ``` and ```json { ... "messages": [ { "role":"system", "content":"You are" }, { "role":"user", "content":"test 1" } { "role":"user", "content":"test 2" } ], ... } ``` are both problematic. The first snippet is what opencode basically sends by default and the template throws an error: `Only user, assistant and tool roles are supported, got system. at row 124, column 111...` for it. The issue seems to be related to https://github.com/sst/opencode/issues/2272.
Author
Owner

@bamer commented on GitHub (Dec 10, 2025):

Still not working, everything is fine on the llame.cpp side since they today but it's still not working with opencode :( and devstrall too ....Is there a quick bypass or fix ?

@bamer commented on GitHub (Dec 10, 2025): Still not working, everything is fine on the llame.cpp side since they today but it's still not working with opencode :( and devstrall too ....Is there a quick bypass or fix ?
Author
Owner

@kurnevsky commented on GitHub (Dec 10, 2025):

For quick fix you can merge all system prompts into one here, or modify mistral chat template and pass it via --chat-template. Would be good if opencode allowed to customize this.

@kurnevsky commented on GitHub (Dec 10, 2025): For quick fix you can merge all system prompts into one [here](https://github.com/sst/opencode/blob/862141e8b22b4de334a1f4883096c325a1508bc5/packages/opencode/src/session/prompt.ts#L684-L685), or modify mistral chat template and pass it via `--chat-template`. Would be good if opencode allowed to customize this.
Author
Owner

@grigio commented on GitHub (Dec 12, 2025):

A workaround is to use Mistral Vibe, but I hope opencode will support those models too

@grigio commented on GitHub (Dec 12, 2025): A workaround is to use Mistral Vibe, but I hope opencode will support those models too
Author
Owner

@rekram1-node commented on GitHub (Dec 16, 2025):

I think this should be fixed? The system prompts should be sent as merged (only exception currently is if u are using anthropic)

@rekram1-node commented on GitHub (Dec 16, 2025): I think this should be fixed? The system prompts should be sent as merged (only exception currently is if u are using anthropic)
Author
Owner

@haydonryan commented on GitHub (Dec 30, 2025):

I'm getting this issue., with opencode 1.0.218 using devstral-2-small.

$ llama-server --version
ggml_vulkan: Found 1 Vulkan devices:
ggml_vulkan: 0 = NVIDIA GeForce RTX 3090 (NVIDIA) | uma: 0 | fp16: 1 | bf16: 1 | warp size: 32 | shared memory: 49152 | int dot: 1 | matrix cores: NV_coopmat2
version: 1087 (66d767f)
built with GNU 15.2.1 for Linux x86_64

llama-server \
    -hf unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF:Q3_K_S \
    --jinja -ngl 99 --threads -1 --ctx-size 16384 \
    --temp 0.15 \
                --no-mmap

errors from llama-server

srv operator(): got exception: {"error":{"code":500,"message":"After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results. at row 189, column 164:\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n
{{- raise_exception('After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results.') }}\n ^\n
{%- endif %}\n at row 189, column 13:\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n {{- raise_exception('After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results.') }}\n ^\n {%- endif %}\n at row 188, column 69:\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n
^\n {{- raise_exception('After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results.') }}\n at row 188, column 9:\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n ^\n
{{- raise_exception('After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results.') }}\n at row 187, column 176:\n{%- for message in loop_messages %}\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n ^\n
{%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n at row 187, column 5:\n{%- for message in loop_messages %}\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n ^\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n at row 186, column 36:\n{%- set ns = namespace(index=0) %}\n{%- for message in loop_messages %}\n ^
\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n at row 186, column 1:\n{%- set ns = namespace(index=0) %}\n{%- for message in loop_messages %}\n^\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n at row 1, column 30:\n{#- Unsloth template fixes #}\n
^\n{%- set yesterday_day = strftime_now("%d") %}\n","type":"server_error"}}

(didn't put it in a code block as it tried to make it one really long line)

@haydonryan commented on GitHub (Dec 30, 2025): I'm getting this issue., with opencode 1.0.218 using devstral-2-small. ``` $ llama-server --version ggml_vulkan: Found 1 Vulkan devices: ggml_vulkan: 0 = NVIDIA GeForce RTX 3090 (NVIDIA) | uma: 0 | fp16: 1 | bf16: 1 | warp size: 32 | shared memory: 49152 | int dot: 1 | matrix cores: NV_coopmat2 version: 1087 (66d767f) built with GNU 15.2.1 for Linux x86_64 llama-server \ -hf unsloth/Devstral-Small-2-24B-Instruct-2512-GGUF:Q3_K_S \ --jinja -ngl 99 --threads -1 --ctx-size 16384 \ --temp 0.15 \ --no-mmap ``` errors from llama-server srv operator(): got exception: {"error":{"code":500,"message":"After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results. at row 189, column 164:\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n {{- raise_exception('After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results.') }}\n ^\n {%- endif %}\n at row 189, column 13:\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n {{- raise_exception('After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results.') }}\n ^\n {%- endif %}\n at row 188, column 69:\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n ^\n {{- raise_exception('After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results.') }}\n at row 188, column 9:\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n ^\n {{- raise_exception('After the optional system message, conversation roles must alternate user and assistant roles except for tool calls and results.') }}\n at row 187, column 176:\n{%- for message in loop_messages %}\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n ^\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n at row 187, column 5:\n{%- for message in loop_messages %}\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n ^\n {%- if (message['role'] == 'user') != (ns.index % 2 == 0) %}\n at row 186, column 36:\n{%- set ns = namespace(index=0) %}\n{%- for message in loop_messages %}\n ^ \n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n at row 186, column 1:\n{%- set ns = namespace(index=0) %}\n{%- for message in loop_messages %}\n^\n {%- if message.role == 'user' or (message.role == 'assistant' and (message.tool_calls is not defined or message.tool_calls is none or message.tool_calls | length == 0)) %}\n at row 1, column 30:\n{#- Unsloth template fixes #}\n ^\n{%- set yesterday_day = strftime_now(\"%d\") %}\n","type":"server_error"}} (didn't put it in a code block as it tried to make it one really long line)
Author
Owner

@lee-b commented on GitHub (Jan 27, 2026):

devstral 2 also affected :/

@lee-b commented on GitHub (Jan 27, 2026): devstral 2 also affected :/
Author
Owner

@lee-b commented on GitHub (Jan 27, 2026):

Well, I don't know. The problem is in default ministral jinja template

I think it's desirable that all models would support non-alternating interactions, but that's probably an assumption too far on opencode's part, since the models are usually trained for back-and-forth dialog. Evidence would be that mistral vibe works, opencode does not.

A quick fix might be to join contiguous responses from either party.

@lee-b commented on GitHub (Jan 27, 2026): > Well, I don't know. The problem is in default ministral jinja template I think it's _desirable_ that all models would support non-alternating interactions, but that's probably an assumption too far on opencode's part, since the models are usually trained for back-and-forth dialog. Evidence would be that mistral vibe works, opencode does not. A quick fix might be to join contiguous responses from either party.
Author
Owner

@jacekpoplawski commented on GitHub (Jan 27, 2026):

This happens also on gemma models, I think this should be fixed in the opencode not in the llama.cpp? Please correct me if I am wrong.

@jacekpoplawski commented on GitHub (Jan 27, 2026): This happens also on gemma models, I think this should be fixed in the opencode not in the llama.cpp? Please correct me if I am wrong.
Author
Owner

@audiserg commented on GitHub (Feb 3, 2026):

gemma3n too

@audiserg commented on GitHub (Feb 3, 2026): gemma3n too
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#3287