Skip to content

When using the qwen3.5-122b-a10b model (via nvidia provider), the following error is returned: System message must be at the beginning #16560

Closed as not planned

Description

@fanyapeng

Description

Environment

  • OpenCode Version: 1.2.21
  • Operating System: Windows
  • Provider: nvidia
  • Model: qwen/qwen3.5-122b-a10b

Steps to Reproduce

  1. Configure opencode.json to use nvidia provider with qwen3.5-122b-a10b model
  2. Start opencode and try to interact with the model
  3. Error occurs: System message must be at the beginning

Workarounds Attempted
Added the following configuration in opencode.json:
{
"nvidia": {
"models": {
"qwen/qwen3.5-122b-a10b": {
"chat_template_kwargs": {
"enable_system_prompt": true
}
}
}
}
}
But the problem persists.

Expected Behavior
The model should be able to receive and process system prompts normally without returning an error.

Additional Information

  • Error occurs every time (100% reproducibility)
  • Other models (e.g., glm-4.7, doubao-seed-2.0-code) work fine with this provider
  • Have not tried direct curl calls to nvidia API yet

Plugins

No response

OpenCode version

No response

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Mar 8, 2026
  2. github-actions commented on Mar 8, 2026

    @github-actions
    Contributor

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

    If your issue is the same underlying problem, consider adding a comment or reaction there rather than opening a new report.

  3. paulgear commented on Mar 8, 2026

    @paulgear

    This is a dup of #5034 (and possibly a recurrence of #1301), and I've produced a PR #15018 to fix it. Please add your upvote so it can get merged.

  4. brainwang commented on Apr 10, 2026

    @brainwang

    Any workaround?

  5. sgmihai commented on Apr 16, 2026

    @sgmihai

    qwen3.6 32b in vllm, getting this message in opencode + omo slim, every now and then

  6. assigned and unassigned on Apr 26, 2026
  7. removed
    bugSomething isn't working
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  8. robertoberto commented on May 23, 2026

    @robertoberto

    same bug as #15059. @paulgear's #15018 was the right shape, and #16981 (currently open and awaiting code-owner review) generalizes it with a capability flag.

    still broken on opencode v1.14.39 against Qwen/Qwen3.6-27B and Qwen/Qwen3.6-35B-A3B on vLLM v0.21.0, even with no plugins — the title-generator path produces [system, system, user] on its own. running the equivalent of #15018 locally resolves the 400s without affecting any other provider.

    diagnosis + repro in #15059.

  9. github-actions commented on Jul 23, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

  10. brusacco commented on Aug 16, 2026

    @brusacco

    Build a little proxy in Node to fix this issue; this is happening now on Qwen 3.8

    `const http = require("http");

    const PORT = 11435;
    const OLLAMA_HOST = "192.168.0.32";
    const OLLAMA_PORT = 11434;

    /**

    • Qwen 3.8 requires system messages to appear only at the beginning.
    • OpenCode may send multiple system messages throughout the message array.
    • This function:
      1. Collects every system message.
      1. Merges their content.
      1. Places one system message at position 0.
      1. Preserves every non-system message unchanged.
        */
        function mergeSystemMessages(messages) {
        if (!Array.isArray(messages)) {
        return messages;
        }
    const systemMessages = [];
    const otherMessages = [];
    
    for (const message of messages) {
        if (message.role === "system") {
            systemMessages.push(message);
        } else {
            otherMessages.push(message);
        }
    }
    
    if (systemMessages.length === 0) {
        return messages;
    }
    
    // Preserve string or structured content.
    const allStrings = systemMessages.every(
        (message) => typeof message.content === "string"
    );
    
    let content;
    
    if (allStrings) {
        content = systemMessages
            .map((message) => message.content)
            .filter(Boolean)
            .join("\n\n");
    } else {
        // Handle OpenAI-style structured content arrays.
        content = systemMessages.flatMap((message) => {
            if (Array.isArray(message.content)) {
                return message.content;
            }
    
            if (typeof message.content === "string") {
                return [
                    {
                        type: "text",
                        text: message.content,
                    },
                ];
            }
    
            return [];
        });
    }
    
    console.log(
        `[proxy] merged ${systemMessages.length} system messages`
    );
    
    return [
        {
            role: "system",
            content,
        },
        ...otherMessages,
    ];
    

    }

    const server = http.createServer((req, res) => {
    // We only need to modify chat completions.
    if (
    req.method === "POST" &&
    req.url.startsWith("/v1/chat/completions")
    ) {
    let body = "";

        req.on("data", (chunk) => {
            body += chunk;
        });
    
        req.on("end", () => {
            try {
                const payload = JSON.parse(body);
    
                if (Array.isArray(payload.messages)) {
                    console.log(
                        `[proxy] ${payload.model} - ${payload.messages.length} messages`
                    );
    
                    console.log(
                        "[proxy] roles:",
                        payload.messages.map((m) => m.role).join(" → ")
                    );
    
                    payload.messages = mergeSystemMessages(payload.messages);
    
                    console.log(
                        "[proxy] forwarded roles:",
                        payload.messages.map((m) => m.role).join(" → ")
                    );
                }
    
                const modifiedBody = JSON.stringify(payload);
    
                const headers = {
                    ...req.headers,
                    host: `${OLLAMA_HOST}:${OLLAMA_PORT}`,
                    "content-length": Buffer.byteLength(modifiedBody),
                };
    
                const proxyReq = http.request(
                    {
                        hostname: OLLAMA_HOST,
                        port: OLLAMA_PORT,
                        path: req.url,
                        method: req.method,
                        headers,
                    },
                    (proxyRes) => {
                        res.writeHead(proxyRes.statusCode, proxyRes.headers);
    
                        // Important:
                        // pipe directly so Ollama SSE streaming remains streaming.
                        proxyRes.pipe(res);
                    }
                );
    
                proxyReq.on("error", (error) => {
                    console.error("[proxy] Ollama error:", error);
    
                    if (!res.headersSent) {
                        res.writeHead(502, {
                            "Content-Type": "application/json",
                        });
                    }
    
                    res.end(
                        JSON.stringify({
                            error: {
                                message: error.message,
                                type: "proxy_error",
                            },
                        })
                    );
                });
    
                proxyReq.write(modifiedBody);
                proxyReq.end();
            } catch (error) {
                console.error("[proxy] Invalid request:", error);
    
                res.writeHead(400, {
                    "Content-Type": "application/json",
                });
    
                res.end(
                    JSON.stringify({
                        error: {
                            message: error.message,
                            type: "proxy_error",
                        },
                    })
                );
            }
        });
    
        return;
    }
    
    /*
     * Everything else is passed through untouched.
     *
     * This lets things such as /v1/models continue working.
     */
    
    const proxyReq = http.request(
        {
            hostname: OLLAMA_HOST,
            port: OLLAMA_PORT,
            path: req.url,
            method: req.method,
            headers: {
                ...req.headers,
                host: `${OLLAMA_HOST}:${OLLAMA_PORT}`,
            },
        },
        (proxyRes) => {
            res.writeHead(proxyRes.statusCode, proxyRes.headers);
            proxyRes.pipe(res);
        }
    );
    
    proxyReq.on("error", (error) => {
        console.error("[proxy] Ollama error:", error);
    
        res.writeHead(502, {
            "Content-Type": "application/json",
        });
    
        res.end(
            JSON.stringify({
                error: {
                    message: error.message,
                    type: "proxy_error",
                },
            })
        );
    });
    
    req.pipe(proxyReq);
    

    });

    server.listen(PORT, "0.0.0.0", () => {
    console.log(Qwen/OpenCode proxy listening on :${PORT});
    console.log(
    Forwarding to http://${OLLAMA_HOST}:${OLLAMA_PORT}
    );
    });`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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