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

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions