openclaw - ✅(Solved) Fix [Bug]: OpenRouter model runs return blank responses / stopReason=stop payloads=0 on 2026.4.15 Summary [1 pull requests, 1 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#68261Fetched 2026-04-18 05:53:27
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
labeled ×2cross-referenced ×1subscribed ×1

On OpenClaw 2026.4.15 (041266a) on macOS, chat responses in the dashboard come back blank. The same failure reproduces outside the dashboard with a direct CLI inference command, so this does not appear to be a stuck session or UI-only issue.

The key log line is:

[agent/embedded] incomplete turn detected: runId=... sessionId=... stopReason=stop payloads=0 — surfacing error to user

Error Message

[agent/embedded] incomplete turn detected: runId=... sessionId=... stopReason=stop payloads=0 — surfacing error to user 15:19:29 [agent/embedded] incomplete turn detected: runId=0d6e341f-3d97-4b3c-9884-8fb3f6dda13b sessionId=0d6e341f-3d97-4b3c-9884-8fb3f6dda13b stopReason=stop payloads=0 — surfacing error to user [agent/embedded] incomplete turn detected: runId=... sessionId=... stopReason=stop payloads=0 — surfacing error to user

Root Cause

On OpenClaw 2026.4.15 (041266a) on macOS, chat responses in the dashboard come back blank. The same failure reproduces outside the dashboard with a direct CLI inference command, so this does not appear to be a stuck session or UI-only issue.

The key log line is:

[agent/embedded] incomplete turn detected: runId=... sessionId=... stopReason=stop payloads=0 — surfacing error to user

Fix Action

Fixed

PR fix notes

PR #68351: fix(openrouter): surface reasoning_details visible-text as user output (#68261)

Description (problem / solution / changelog)

Summary

  • Problem: OpenRouter model runs (e.g. openrouter/google/gemini-2.5-flash, Grok 4.x, minimax-m2.7) return blank responses with stopReason=stop payloads=0 and surface ⚠️ Agent couldn't generate a response.
  • Why it matters: Entire classes of OpenRouter-proxied models have been unusable since 2026.4.14/15 despite healthy gateway and successful upstream responses.
  • What changed: getCompletionsReasoningDelta in src/agents/openai-transport-stream.ts now discriminates the OpenRouter reasoning_details stream between hidden reasoning (reasoning.text) and OpenAI Responses–style visible output (response.output_text, response.text, text). Visible-output items are routed through the same text-block path as choice.delta.content instead of being buried in a thinking block.
  • What did NOT change (scope boundary): no changes to prompts, auth, sandbox, tool policy, secret handling, provider routing, request assembly, transcript bytes, or prompt-cache order. Previous reasoning.text behavior is preserved byte-for-byte.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #68261
  • Related #67410, #67575, #67698, #67907, #67425, #68030, #68076, #68185
  • This PR fixes a bug or regression

Root Cause

e0bf756b50 ("fix: handle OpenRouter Qwen3 reasoning_details streams") taught getCompletionsReasoningDelta to parse reasoning_details items, but it unconditionally classified every matching item as hidden thinking. Some OpenRouter upstreams (Gemini 2.5 Flash via the Responses-style reasoning proxy, xAI Grok 4.x, and related models) deliver the assistant's final answer inside reasoning_details using OpenAI Responses content types such as response.output_text, response.text, and plain text, with an empty choice.delta.content. Because the parser treated those as thinking, the stream closed with stopReason=stop, zero text blocks, and the incomplete-turn detector in src/agents/pi-embedded-runner/run/incomplete-turn.ts surfaced the generic error.

  • Root cause: reasoning_details visible-output types were misclassified as internal reasoning and never emitted as user-visible text.
  • Missing detection / guardrail: no regression covered reasoning_details streams where the final answer arrives without a separate content field.
  • Contributing context: OpenRouter's proxy layer for several reasoning models started folding visible output into reasoning_details alongside reasoning bytes.

Regression Test Plan

  • Coverage level that should have caught this:
    • Unit test
  • Target test or file: src/agents/openai-transport-stream.test.ts
  • Scenario the test should lock in: a mixed reasoning_details stream where reasoning.text items stay as thinking while response.output_text, response.text, and text items surface as a user-visible text block with stopReason=stop.
  • Why this is the smallest reliable guardrail: it exercises the exact parser path that decides whether bytes become visible text or hidden thinking.
  • Existing test that already covers this: handles reasoning_details from OpenRouter/Qwen3 in completions stream only covered content + reasoning.text mixes, not the visible-text-in-reasoning_details case.

User-visible / Behavior Changes

  • OpenRouter models that deliver the final answer inside reasoning_details (e.g. Gemini 2.5 Flash, Grok 4.x) now produce visible replies instead of the generic ⚠️ Agent couldn't generate a response error. No configuration, defaults, or docs change.

Diagram

Before:
OpenRouter -> reasoning_details[{type:"response.output_text", text:"hello"}]
           -> classified as thinking
           -> stopReason=stop, payloads=0
           -> "⚠️ Agent couldn't generate a response."

After:
OpenRouter -> reasoning_details[{type:"response.output_text", text:"hello"}]
           -> classified as visible text
           -> text block "hello", stopReason=stop, payloads>=1
           -> user sees "hello"

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Runtime-enforced controls that are explicitly unchanged by this PR: tool policy/allowlist, sandbox, exec approval, subagent policy, secrets plan, owner-only gating, OpenRouter routing, auth profiles, and prompt-cache request assembly. The fix is confined to classification of bytes the upstream already returned over an authenticated, already-policy-gated channel; it does not rely on or modify any prompt text or runtime policy. A malicious upstream that labels reasoning as type: "text" cannot leak more than it could by setting choice.delta.content directly, which is already the default visible path.

Repro + Verification

Environment

  • OS: macOS (Darwin 25.3.0)
  • Runtime/container: local OpenClaw gateway (loopback)
  • Model/provider: openrouter/google/gemini-2.5-flash, openrouter/x-ai/grok-4.1-fast, openrouter/minimax/minimax-m2.7
  • Integration/channel: none (direct openclaw infer model run) and dashboard
  • Relevant config: agents.defaults.models = { "openrouter/google/gemini-2.5-flash": {} } (redacted)

Steps

  1. openclaw infer model run --model openrouter/google/gemini-2.5-flash --prompt "Reply with exactly: hello" --json
  2. Observe response contents and gateway log for incomplete turn detected ... payloads=0.
  3. Repeat for Grok 4.x / minimax-m2.7.

Expected

  • outputs[0].text contains the assistant reply.
  • No payloads=0 / incomplete turn detected warning.

Actual (before fix)

  • outputs[0].text = ⚠️ Agent couldn't generate a response. Please try again.
  • Gateway log: incomplete turn detected: ... stopReason=stop payloads=0 — surfacing error to user.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets

Before: reasoning_details visible-text items classified as thinking, zero text blocks, stopReason=stop payloads=0. After: new test surfaces reasoning_details visible-text as text while keeping reasoning.text as thinking asserts text block emission alongside the preserved thinking block; existing reasoning_details/Qwen3 and tool-call regression tests remain green.

Human Verification (required)

  • Verified scenarios:
    • pnpm test src/agents/openai-transport-stream.test.ts — 56/56 pass including the new case.
    • pnpm test src/agents/openai-transport-stream.test.ts -t reasoning_details — 4/4 reasoning_details cases pass.
    • Re-ran pnpm check (host-env-policy, tool-display, no-conflict-markers green); remaining pnpm tsgo/pnpm lint failures are pre-existing in extensions/qa-lab/src/providers/aimock/server.ts on upstream/main and unrelated to this change (verified by running pnpm tsgo on clean upstream/main).
  • Edge cases checked:
    • reasoning.text-only stream still becomes a thinking block (existing Qwen3 test).
    • Mixed reasoning.text + visible-text items in the same stream produce both thinking and text blocks in order.
    • reasoning_details arriving alongside tool_calls still routes through the buffered thinking path and preserves tool-call arguments (existing tests).
    • Non-array or unknown-type reasoning_details items are skipped silently.
  • What I did not verify:
    • Live OpenRouter run against real keys (no live lane invoked in this change).
    • Parallels smoke / cross-platform onboarding (not in scope for a parser-only fix).

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: a model intentionally using type: "text" inside reasoning_details for genuinely-hidden scratchpad content would now surface that content to users.
    • Mitigation: OpenRouter's documented semantics for response.output_text / response.text / text are visible assistant output; the existing reasoning.text type still maps to hidden reasoning. If a specific upstream deviates, follow-up can narrow the visible-type set.
  • Risk: Qwen3 regression from #66905 returns.
    • Mitigation: the existing Qwen3 regression test and the tool-call + reasoning_details test remain unchanged and still pass.

Testing notes

  • AI-assisted PR, lightly tested (parser unit tests only; no live OpenRouter run).
  • Exact tests run:
    • pnpm test src/agents/openai-transport-stream.test.ts (56/56 pass)
    • pnpm test src/agents/openai-transport-stream.test.ts -t reasoning_details (4/4 pass)
    • pnpm test src/agents/openai-transport-stream.test.ts -t visible-text (1/1 pass)
    • pnpm check (pre-existing aimock/server.ts failures on upstream/main; no new failures introduced)

Made with Cursor

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/agents/openai-transport-stream.test.ts (modified, +138/-0)
  • src/agents/openai-transport-stream.ts (modified, +63/-14)

Code Example

Happy to provide:

- full openclaw gateway status
- openclaw doctor
- openclaw models status
- full gateway logs around the failed run
RAW_BUFFERClick to expand / collapse

Bug type

Regression (worked before, now fails)

Beta release blocker

No

Summary

On OpenClaw 2026.4.15 (041266a) on macOS, chat responses in the dashboard come back blank. The same failure reproduces outside the dashboard with a direct CLI inference command, so this does not appear to be a stuck session or UI-only issue.

The key log line is:

[agent/embedded] incomplete turn detected: runId=... sessionId=... stopReason=stop payloads=0 — surfacing error to user

Steps to reproduce

1) Confirm configured models openclaw config get agents.defaults.models

Output: { "openrouter/google/gemini-2.5-flash": {} }

2) Reproduce with direct inference openclaw infer model run --model openrouter/google/gemini-2.5-flash --prompt "Reply with exactly: hello" --json

Output: 15:19:29 [agent/embedded] incomplete turn detected: runId=0d6e341f-3d97-4b3c-9884-8fb3f6dda13b sessionId=0d6e341f-3d97-4b3c-9884-8fb3f6dda13b stopReason=stop payloads=0 — surfacing error to user { "ok": true, "capability": "model.run", "transport": "local", "provider": "openrouter", "model": "google/gemini-2.5-flash", "attempts": [], "outputs": [ { "text": "⚠️ Agent couldn't generate a response. Please try again.", "mediaUrl": null } ] }

Expected behavior

A simple prompt like Reply with exactly: hello should return hello.

Actual behavior

The dashboard shows a blank response.

Direct CLI inference also fails and returns: { "ok": true, "capability": "model.run", "transport": "local", "provider": "openrouter", "model": "google/gemini-2.5-flash", "attempts": [], "outputs": [ { "text": "⚠️ Agent couldn't generate a response. Please try again.", "mediaUrl": null } ] }

At the same time, logs show: [agent/embedded] incomplete turn detected: runId=... sessionId=... stopReason=stop payloads=0 — surfacing error to user

OpenClaw version

2026.4.15 (041266a)

Operating system

macOS

Install method

No response

Model

openrouter/google/gemini-2.5-flash

Provider / routing chain

Local OpenClaw gateway -> OpenRouter provider -> google/gemini-2.5-flash

Additional provider/model setup details

No response

Logs, screenshots, and evidence

Happy to provide:

- full openclaw gateway status
- openclaw doctor
- openclaw models status
- full gateway logs around the failed run

Impact and severity

No response

Additional information

Additional diagnostics Gateway status openclaw gateway status

Gateway appears healthy:

  • LaunchAgent loaded
  • runtime running
  • RPC probe ok
  • listening on 127.0.0.1:18789

Doctor openclaw doctor

Nothing obvious indicates a broken gateway. Session store shows only 1 session entry for main.

Models status openclaw models status

Shows OpenRouter configured via environment variable.

Notes / things already tried

  • Tried clearing the session initially, but this does not appear to be a stuck session issue.
  • Reproduced the same failure in direct CLI inference, outside the dashboard.
  • Ran openclaw update, but package manager remained on 2026.4.15.
  • Restarted gateway.
  • Reconfigured model selection so agents.defaults.models contains only:
  • openrouter/google/gemini-2.5-flash
  • Retested after gateway restart and still got the same payloads=0 failure.

Possible clue During earlier gateway startup, I also saw the runtime report: [gateway] agent model: openrouter/openrouter/auto

even though the intended model was openrouter/google/gemini-2.5-flash.

After reconfiguring, agents.defaults.models now only shows the pinned Gemini model, but inference still fails the same way.

Question Is this a regression in the OpenRouter path on 2026.4.15, or is there another config/runtime resolution issue that can produce stopReason=stop payloads=0 with otherwise healthy gateway status?

extent analysis

TL;DR

The issue might be related to a configuration or model resolution problem, and checking the model configuration and the OpenRouter setup could provide more insights.

Guidance

  • Verify that the openrouter/google/gemini-2.5-flash model is correctly configured and available in the OpenClaw setup.
  • Check the OpenRouter provider configuration to ensure it is pointing to the correct model and version.
  • Investigate the earlier gateway startup log message indicating the use of openrouter/openrouter/auto instead of the intended openrouter/google/gemini-2.5-flash model.
  • Run openclaw models status and openclaw doctor again to see if there are any issues with the model or gateway configuration.

Example

No specific code example is provided as the issue seems to be related to configuration and model resolution.

Notes

The issue might be specific to the OpenClaw version (2026.4.15) or the OpenRouter provider setup. More information about the OpenRouter configuration and the model setup might be necessary to resolve the issue.

Recommendation

Apply workaround: Try reconfiguring the agents.defaults.models to use a different model or version to see if the issue persists, and investigate the OpenRouter provider setup to ensure it is correctly configured.

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…

FAQ

Expected behavior

A simple prompt like Reply with exactly: hello should return hello.

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]: OpenRouter model runs return blank responses / stopReason=stop payloads=0 on 2026.4.15 Summary [1 pull requests, 1 participants]