openclaw - ✅(Solved) Fix [Bug]: ZAI GLM-5 reasoning models return empty responses — reasoning_content routed to thinking instead of text (regression from 2026.4.24+) [1 pull requests, 3 comments, 4 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#76048Fetched 2026-05-03 04:42:53
View on GitHub
Comments
3
Participants
4
Timeline
11
Reactions
2
Author
Timeline (top)
referenced ×4commented ×3cross-referenced ×3unsubscribed ×1

ZAI GLM-5 reasoning models (glm-5-turbo, glm-5.1) return empty/incoherent responses in OpenClaw 2026.4.29. The API returns all actual text in reasoning_content field with content="", but the stream processing pipeline routes reasoning_content to appendThinkingDelta() (hidden) instead of appendTextDelta() (visible text).

Error Message

  • #73688 — ZAI/GLM session creation 400 error

Root Cause

  • #75105 — Feature request for per-model compat.reasoningFormat setting (same architectural gap)
  • #76018 — DeepSeek V4 via OpenRouter fails because reasoning_content wrapper only applies to provider=deepseek
  • #75040 — extra_body thinking field collision affecting all providers
  • #74886 — Thinking traces leaked to WhatsApp (inverse symptom, same root cause area)
  • #73683 — ZAI GLM-5.1 session bootstrap race condition
  • #73688 — ZAI/GLM session creation 400 error

Fix Action

Fix / Workaround

Current workaround

Patching openai-transport-stream-aPa0aR5w.js with the above change (in both npm and plugin-runtime-deps paths). Patch is overwritten on npm update openclaw.

PR fix notes

PR #76078: fix(providers/zai): route reasoning_content to visible text for GLM-5 reasoning models

Description (problem / solution / changelog)

Fixes #76048.

Root cause

getCompletionsReasoningDeltas tagged every reasoning_content / reasoning / reasoning_text field as kind: "thinking" regardless of provider. For all providers except Z.AI this is correct, but GLM-5 reasoning models always place their visible response in reasoning_content (with content: ""), so the stream pipeline was routing the user-facing text to appendThinkingDelta (hidden) and sending an empty reply.

The thinkingFormat: "zai" compat flag was already set correctly by resolveOpenAICompletionsCompatDefaults, but the stream routing never consumed it.

Fix

Pass thinkingFormat to getCompletionsReasoningDeltas. When thinkingFormat === "zai", tag reasoning_content as kind: "text" so it reaches appendTextDelta (visible). All other providers are unchanged.

src/agents/openai-transport-stream.ts — add thinkingFormat? param to getCompletionsReasoningDeltas; route Z.AI reasoning_content to "text" kind. Pass compat.thinkingFormat at the call site (already in scope).

Test

New test in src/agents/openai-transport-stream.test.ts:

  • glm-5-turbo / provider: "zai" stream with content: "" + reasoning_content: "..." → single text block reaching the user.

97/97 transport-stream tests + 9/9 ZAI extension tests pass. oxlint clean.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/agents/openai-transport-stream.test.ts (modified, +162/-0)
  • src/agents/openai-transport-stream.ts (modified, +17/-2)

Code Example

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "",
      "reasoning_content": "This is the actual user-facing text the model generated"
    }
  }],
  "usage": { "completion_tokens": 200, "reasoning_tokens": 198 }
}

---

// In processOpenAICompletionsStream(), ~line 1845:
// Before (broken for ZAI):
if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text);
else appendThinkingDelta(reasoningDelta);

// After (respects thinkingFormat compat):
if (reasoningDelta.kind === "text" || compat.thinkingFormat === "zai") appendTextDelta(reasoningDelta.text);
else appendThinkingDelta(reasoningDelta);
RAW_BUFFERClick to expand / collapse

Summary

ZAI GLM-5 reasoning models (glm-5-turbo, glm-5.1) return empty/incoherent responses in OpenClaw 2026.4.29. The API returns all actual text in reasoning_content field with content="", but the stream processing pipeline routes reasoning_content to appendThinkingDelta() (hidden) instead of appendTextDelta() (visible text).

Environment

  • OpenClaw: 2026.4.29 (latest as of 2026-05-02)
  • Primary model: zai/glm-5-turbo
  • Channel: Feishu (likely affects all channels)
  • ZAI baseUrl: https://open.bigmodel.cn/api/coding/paas/v4

Problem to solve

ZAI API response format

GLM-5 reasoning models return responses like:

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "",
      "reasoning_content": "This is the actual user-facing text the model generated"
    }
  }],
  "usage": { "completion_tokens": 200, "reasoning_tokens": 198 }
}

All meaningful text is in reasoning_content, while content is empty.

OpenClaw stream processing behavior

In openai-transport-stream-aPa0aR5w.js, the stream pipeline:

  1. processOpenAICompletionsStream()getCompletionsReasoningDeltas() → captures reasoning_content from delta chunks
  2. getCompletionsReasoningDeltas() tags these as kind: "thinking"
  3. The routing at ~line 1845 sends them to appendThinkingDelta() (hidden from user)
  4. Since content="", no text reaches appendTextDelta() (visible to user)
  5. Result: empty response sent to user

The dead compat flag

provider-model-compat-*.js correctly sets thinkingFormat: "zai" for ZAI provider. The getCompat() function merges this into the compat object. However, the stream processing code never checks compat.thinkingFormat when deciding whether to route reasoning_content as text vs thinking.

The requiresThinkingAsText compat flag also exists in the Zod schema and model config normalization, but is similarly never consumed by any stream processing code path.

ZAI plugin behavior

The ZAI plugin's resolveThinkingProfile() returns defaultLevel: "off", which sends payload.thinking = { type: "disabled" } to the API. However, GLM-5 reasoning models still produce reasoning_content regardless of this setting.

Regression

This worked correctly before v2026.4.24. The stream processing pipeline was restructured around that version to support thinking/reasoning across providers, and ZAI's reasoning_content handling was broken as a result.

Related issues

  • #75105 — Feature request for per-model compat.reasoningFormat setting (same architectural gap)
  • #76018 — DeepSeek V4 via OpenRouter fails because reasoning_content wrapper only applies to provider=deepseek
  • #75040 — extra_body thinking field collision affecting all providers
  • #74886 — Thinking traces leaked to WhatsApp (inverse symptom, same root cause area)
  • #73683 — ZAI GLM-5.1 session bootstrap race condition
  • #73688 — ZAI/GLM session creation 400 error

Proposed fix

The thinkingFormat compat flag already exists and is correctly set to "zai" for ZAI provider. The fix should consume this flag in the stream processing routing decision:

// In processOpenAICompletionsStream(), ~line 1845:
// Before (broken for ZAI):
if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text);
else appendThinkingDelta(reasoningDelta);

// After (respects thinkingFormat compat):
if (reasoningDelta.kind === "text" || compat.thinkingFormat === "zai") appendTextDelta(reasoningDelta.text);
else appendThinkingDelta(reasoningDelta);

This is consistent with the direction proposed in #75105 — using compat flags to control per-provider reasoning behavior rather than hardcoding to specific provider/model names.

Current workaround

Patching openai-transport-stream-aPa0aR5w.js with the above change (in both npm and plugin-runtime-deps paths). Patch is overwritten on npm update openclaw.

extent analysis

TL;DR

The most likely fix is to update the stream processing routing decision to respect the thinkingFormat compat flag, specifically for the ZAI provider.

Guidance

  • Verify that the thinkingFormat compat flag is correctly set to "zai" for the ZAI provider in the provider-model-compat-*.js file.
  • Update the processOpenAICompletionsStream() function in openai-transport-stream-aPa0aR5w.js to consume the compat.thinkingFormat flag when deciding whether to route reasoning_content as text or thinking.
  • Test the updated code to ensure that the reasoning_content is correctly routed to appendTextDelta() for the ZAI provider.
  • Consider implementing a more robust solution, such as using a per-model compat.reasoningFormat setting, as proposed in #75105.

Example

// In processOpenAICompletionsStream(), ~line 1845:
if (reasoningDelta.kind === "text" || compat.thinkingFormat === "zai") appendTextDelta(reasoningDelta.text);
else appendThinkingDelta(reasoningDelta);

Notes

The proposed fix is a temporary workaround, and a more permanent solution may require updates to the OpenClaw codebase. Additionally, this fix may not address other related issues, such as #76018 and #75040.

Recommendation

Apply the proposed workaround by updating the processOpenAICompletionsStream() function to respect the thinkingFormat compat flag. This should fix the issue for the ZAI provider, but a more robust solution may be needed in the long term.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: ZAI GLM-5 reasoning models return empty responses — reasoning_content routed to thinking instead of text (regression from 2026.4.24+) [1 pull requests, 3 comments, 4 participants]