Description
Description
When a plugin uses the experimental.chat.system.transform hook to append to the output.system array, OpenCode creates multiple {role: "system"} messages in the messages array. OpenAI-compatible providers (non-OpenAI) reject this with:
A 'system' message can only appear at index 0 of the messages array. Move it to the start of the conversation.
Root Cause
In packages/opencode/src/session/llm/request.ts, the post-hook collapse logic only triggers when system.length > 2:
if (system.length > 2 && system[0] === header) {
const rest = system.slice(1)
system.length = 0
system.push(header, rest.join("\n"))
}
The system array starts as 1 element. A plugin that pushes 1 entry makes it 2 elements. Since 2 > 2 is false, the collapse never fires, resulting in 2 separate {role: "system"} messages — which OpenAI-compatible endpoints reject.
Fix
Change the condition from > 2 to > 1:
if (system.length > 1 && system[0] === header) {
This ensures that when any plugin adds entries, they are joined into a single system message, compatible with all providers.
Workaround
Modify the plugin to concatenate to output.system[0] instead of pushing:
// Instead of:
output.system.push(getPonytailInstructions(mode));
// Use:
output.system[0] = (output.system[0] || '') + '\n\n' + getPonytailInstructions(mode);
Affected
- Any OpenAI-compatible provider (custom endpoints, proxied APIs)
- Any plugin that uses experimental.chat.system.transform with .push()
- Tested with @dietrichgebert/ponytail plugin
Plugins
@dietrichgebert/ponytail
OpenCode version
1.17.11
Steps to reproduce
- Install @dietrichgebert/ponytail plugin
- Configure an OpenAI-compatible provider (non-OpenAI) in opencode.json (e.g. custom endpoint, proxied API)
- Send any message
Expected: Message is processed normally
Actual: Error "A 'system' message can only appear at index 0 of the messages array"
Screenshot and/or share link
Operating System
Windows 11
Terminal
Windows Terminal
Description
Description
When a plugin uses the
experimental.chat.system.transformhook to append to theoutput.systemarray, OpenCode creates multiple{role: "system"}messages in the messages array. OpenAI-compatible providers (non-OpenAI) reject this with:Root Cause
In
packages/opencode/src/session/llm/request.ts, the post-hook collapse logic only triggers whensystem.length > 2:The system array starts as 1 element. A plugin that pushes 1 entry makes it 2 elements. Since 2 > 2 is false, the collapse never fires, resulting in 2 separate {role: "system"} messages — which OpenAI-compatible endpoints reject.
Fix
Change the condition from > 2 to > 1:
if (system.length > 1 && system[0] === header) {
This ensures that when any plugin adds entries, they are joined into a single system message, compatible with all providers.
Workaround
Modify the plugin to concatenate to output.system[0] instead of pushing:
// Instead of:
output.system.push(getPonytailInstructions(mode));
// Use:
output.system[0] = (output.system[0] || '') + '\n\n' + getPonytailInstructions(mode);
Affected
Plugins
@dietrichgebert/ponytail
OpenCode version
1.17.11
Steps to reproduce
Expected: Message is processed normally
Actual: Error "A 'system' message can only appear at index 0 of the messages array"
Screenshot and/or share link
Operating System
Windows 11
Terminal
Windows Terminal