Summary
The placeholder string [System: Empty message content sanitised to satisfy protocol] is still appearing in conversations. This was previously reported in #17564 and closed as completed, but the issue persists (or has regressed) when using GLM-5.2 via an OpenAI-compatible provider (@ai-sdk/openai-compatible).
Environment
- OpenCode: latest (as of 2026-06-22)
- Provider:
xiaofengche (custom OpenAI-compatible provider, @ai-sdk/openai-compatible)
- baseURL:
https://api.svips.org
- Model:
glm-5.2 (GLM-5.2 by Zhipu AI)
- Agent mode:
build (primary)
- OS: Windows
Root Cause (same as #17564)
As identified in #17564, when the Vercel AI SDK converts ModelMessage[] to the OpenAI wire format, assistant messages that contain only tool calls get content: "" (empty string):
// @ai-sdk/openai-compatible
messages.push({
role: 'assistant',
content: text, // "" when assistant only used tools
tool_calls: toolCalls,
})
OpenCode has a guard in packages/opencode/src/provider/transform.ts (normalizeMessages), but per #17564 it was reportedly extended to all providers. However, the placeholder still appears with GLM-5.2.
What happens
GLM-5.2 (via the OpenAI-compatible proxy at api.svips.org) behaves similarly to the LiteLLM case: it replaces empty content fields with the placeholder string [System: Empty message content sanitised to satisfy protocol] before forwarding. This modified message is then stored in conversation history and displayed to the user on every tool-call turn.
This happens because GLM-5.2 tends to emit tool calls without any accompanying text, unlike Claude/GPT which typically output a short explanation before calling tools.
Evidence from logs
OpenCode log file shows the model streaming and the placeholder appearing on every tool-call turn:
INFO service=llm providerID=xiaofengche modelID=glm-5.2 session.id=ses_113992737ffebYGPPQBkNvI4cK small=false agent=build mode=primary stream
And the user-visible output contains [System: Empty message content sanitised to satisfy protocol] repeatedly between tool calls.
Impact
- Visual noise: Users see system placeholder text instead of clean tool-call transitions
- Context pollution: The placeholder text is stored in conversation history and sent back to the model on subsequent turns, wasting context tokens
- User confusion: Users may think the model or framework is malfunctioning
Expected Behavior
Assistant messages with only tool calls should not have empty content sent to the API. The normalizeMessages empty-content filter should apply to all providers unconditionally, as originally proposed in #17564.
Suggested Fix
Per #17564, the fix in packages/opencode/src/provider/transform.ts should ensure the empty content filter runs unconditionally (not gated by provider npm package name):
// Should run for ALL providers, not just @ai-sdk/anthropic / @ai-sdk/amazon-bedrock
msgs = msgs.map(/* filter empty content */).filter(...)
And in packages/opencode/src/session/llm.ts, empty system prompt strings should be filtered:
...system.filter((x) => x !== "").map((x): ModelMessage => ({ role: "system", content: x })),
Related Issues
Reproduction
- Configure OpenCode with an OpenAI-compatible provider pointing to a proxy that routes to GLM-5.2 (e.g.
https://api.svips.org)
- Ask OpenCode any question that requires tool use (e.g. "list files in the current directory")
- Observe
[System: Empty message content sanitised to satisfy protocol] appearing in the response on every tool-call turn
This affects every tool-call turn because GLM-5.2 emits tool calls without accompanying text.
Summary
The placeholder string
[System: Empty message content sanitised to satisfy protocol]is still appearing in conversations. This was previously reported in #17564 and closed as completed, but the issue persists (or has regressed) when using GLM-5.2 via an OpenAI-compatible provider (@ai-sdk/openai-compatible).Environment
xiaofengche(custom OpenAI-compatible provider,@ai-sdk/openai-compatible)https://api.svips.orgglm-5.2(GLM-5.2 by Zhipu AI)build(primary)Root Cause (same as #17564)
As identified in #17564, when the Vercel AI SDK converts
ModelMessage[]to the OpenAI wire format, assistant messages that contain only tool calls getcontent: ""(empty string):OpenCode has a guard in
packages/opencode/src/provider/transform.ts(normalizeMessages), but per #17564 it was reportedly extended to all providers. However, the placeholder still appears with GLM-5.2.What happens
GLM-5.2 (via the OpenAI-compatible proxy at
api.svips.org) behaves similarly to the LiteLLM case: it replaces emptycontentfields with the placeholder string[System: Empty message content sanitised to satisfy protocol]before forwarding. This modified message is then stored in conversation history and displayed to the user on every tool-call turn.This happens because GLM-5.2 tends to emit tool calls without any accompanying text, unlike Claude/GPT which typically output a short explanation before calling tools.
Evidence from logs
OpenCode log file shows the model streaming and the placeholder appearing on every tool-call turn:
And the user-visible output contains
[System: Empty message content sanitised to satisfy protocol]repeatedly between tool calls.Impact
Expected Behavior
Assistant messages with only tool calls should not have empty
contentsent to the API. ThenormalizeMessagesempty-content filter should apply to all providers unconditionally, as originally proposed in #17564.Suggested Fix
Per #17564, the fix in
packages/opencode/src/provider/transform.tsshould ensure the empty content filter runs unconditionally (not gated by provider npm package name):And in
packages/opencode/src/session/llm.ts, empty system prompt strings should be filtered:Related Issues
Reproduction
https://api.svips.org)[System: Empty message content sanitised to satisfy protocol]appearing in the response on every tool-call turnThis affects every tool-call turn because GLM-5.2 emits tool calls without accompanying text.