Skip to content

[regression] [System: Empty message content sanitised to satisfy protocol] still appears with GLM-5.2 via OpenAI-compatible provider #33280

Description

@wc4440222

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

  1. Configure OpenCode with an OpenAI-compatible provider pointing to a proxy that routes to GLM-5.2 (e.g. https://api.svips.org)
  2. Ask OpenCode any question that requires tool use (e.g. "list files in the current directory")
  3. 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.

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