openclaw - ✅(Solved) Fix [Bug]: Incomplete turn false positive — messages sent but error still surfaced (payloads=0 with output) [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#70396Fetched 2026-04-23 07:25:21
View on GitHub
Comments
0
Participants
1
Timeline
5
Reactions
0
Participants
Timeline (top)
cross-referenced ×2referenced ×2closed ×1

OpenClaw detects an "incomplete turn" (stopReason=stop payloads=0) and surfaces the error "⚠️ Agent couldn't generate a response" to the user. The agent generated and attempted to send multiple response messages (confirmed by gateway logs showing telegram sendMessage ok), but the user never received them. The turn completion detection logic correctly flagged an empty assistant payload, but the underlying issue is that messages marked as "sent" by the gateway never reached the user.

This is distinct from other payloads=0 issues — the model DID produce output, and the gateway DID report successful sends, but Telegram never delivered the messages to the user.

Error Message

Bug Report: "Incomplete Turn" False Positive — Messages Sent But Error Still Surfaced

OpenClaw detects an "incomplete turn" (stopReason=stop payloads=0) and surfaces the error "⚠️ Agent couldn't generate a response" to the user. The agent generated and attempted to send multiple response messages (confirmed by gateway logs showing telegram sendMessage ok), but the user never received them. The turn completion detection logic correctly flagged an empty assistant payload, but the underlying issue is that messages marked as "sent" by the gateway never reached the user. | 01:53:31 | Error surfaced to user | "⚠️ Agent couldn't generate a response" | | 01:53:31 | Error message #23670 sent | telegram sendMessage ok chat=2077431888 message=23670 | Then OpenClaw flagged the turn as payloads=0 (because the final assistant payload was empty after tool calls) and showed an error — which was technically correct about the empty payload, but the real issue is the undelivered messages. "message": "incomplete turn detected: runId=a7615325-3fc6-45c3-a9d3-83c8be4944c5 sessionId=d0139187-8cec-43cb-afa9-953080687b3b stopReason=stop payloads=0 — surfacing error to user",

Gateway/Telegram (showing successful sends BEFORE the error)

  1. Despite messages already being sent, error is surfaced Before surfacing the error, check if any messages were actually sent to the user during the turn. If yes, suppress the error.
  • #68076: Incomplete turn with exact payloads=0 error (different — no output at all)
  • The error was not a model timeout — the model completed its reasoning and sent all messages
  • Log excerpts showing successful message sends AND subsequent error

Root Cause

Then OpenClaw flagged the turn as payloads=0 (because the final assistant payload was empty after tool calls) and showed an error — which was technically correct about the empty payload, but the real issue is the undelivered messages.

Fix Action

Fix / Workaround

Workarounds

PR fix notes

PR #70425: fix(pi-embedded-runner): suppress incomplete-turn warning after clean messaging-tool delivery

Description (problem / solution / changelog)

Fixes #70396.

Summary

When the Pi embedded agent runner finishes a turn with payloads=0 but the assistant has already delivered user-visible content through a messaging tool (didSendViaMessagingTool=true) and the model reports a clean terminal state (stopReason=stop), the runner was still surfacing a user-facing error:

⚠️ Agent couldn't generate a response. Note: some tool actions may have already been executed — please verify before retrying.

That warning was a false positive — the user had already received the reply via the messaging tool. It was especially confusing on channels like Telegram where users also saw the bubble in their chat alongside the actual assistant message, implying something had failed.

This change adds a narrow early return in resolveIncompleteTurnPayloadText for exactly that "clean + already delivered" case. Every other incomplete-turn path is unchanged.

Root cause

resolveIncompleteTurnPayloadText deliberately does not treat messaging-tool sends as user-visible payloads (payloadCount is derived from direct assistant payloads only, not from messaging tool output). That is correct — those payloads are delivered out-of-band by the channel. But when the flag didSendViaMessagingTool=true is combined with a clean stopReason=stop and no lastToolError, the function still fell through into the reasoning-only / empty-terminal warning branches, producing the false-positive warning. The scenario was also locked in by run.incomplete-turn.test.ts asserting that the warning fires.

Fix

In src/agents/pi-embedded-runner/run/incomplete-turn.ts, after the early exits for abort, timeout, compaction-timeout, prompt errors, payload count, deterministic approval prompts, yields, client tool calls, and silent replies, add one more:

if (
  params.attempt.didSendViaMessagingTool &&
  !params.attempt.lastToolError &&
  stopReason === "stop"
) {
  return null;
}

All three conditions together are required:

  • didSendViaMessagingTool: the messaging tool executed and reported a successful send during this turn.
  • !lastToolError: no tool error was recorded; a tool error still routes to the existing "verify before retrying" warning.
  • stopReason === "stop": the upstream provider ended the turn cleanly. StopReason from @mariozechner/pi-ai does not include end_turn; toolUse/length/error/aborted all continue to hit their existing branches.

Why the fix is safe

  • The suppression is scoped to a narrow, positively-evidenced "delivery succeeded" state. It does not silence any real error path:
    • stopReason=error → unchanged, still shows "verify before retrying".
    • stopReason=toolUse with tool-use interrupted → unchanged, still shows the warning.
    • Aborted / timed-out / prompt error → unchanged (earlier guards already return).
    • Reasoning-only or empty-response turns without didSendViaMessagingTool → unchanged.
  • replayMetadata.replaySafe already reflects hadPotentialSideEffects, so abandoned-run / replay-invalid tracking (resolveReplayInvalidFlag, resolveRunLivenessState) still treat this attempt as having had side effects. Future retries continue to be gated correctly.
  • The existing test in run.incomplete-turn.test.ts that previously asserted the warning when a messaging tool had already delivered and the turn ended cleanly was encoding the buggy behavior; it is updated to assert the corrected behavior. Other regression tests covering tool errors, interrupted tool use, reasoning-only retries, and silent replies all continue to pass unmodified.

Security / runtime controls unchanged

  • No change to tool approval, policy gating, sandbox, or runtime-enforced permissions. Messaging-tool sends still go through the normal tool pipeline before didSendViaMessagingTool is ever set.
  • No change to system prompts, instructions, or any behavior that depends on prompt text — the gate is on runtime state (didSendViaMessagingTool, lastToolError, stopReason), not on prompt content.
  • No new exports, no new configuration keys, no doc/help/generated-artifact drift.
  • No change to lazy/module boundaries; no pnpm build surface affected.
  • Bundled-plugin and channel boundaries unaffected; change is confined to src/agents/pi-embedded-runner/run/** core runner logic.

Tests run

  • pnpm test src/agents/pi-embedded-runner/run.incomplete-turn.test.ts — 51 passed (49 existing + 2 new targeted cases for the new gate).
  • pnpm test src/agents/pi-embedded-runner/run.empty-error-retry.test.ts src/agents/pi-embedded-runner/run.overflow-compaction.test.ts — 19 passed.
  • pnpm test src/agents/pi-embedded-runner/ — 645 passed across 54 test files.
  • pnpm check:changed --staged (core prod typecheck + core lint + import-cycle / webhook / pairing guards + changed-scope tests) — 287 tests passed across 17 files, all guards green.

Behavior

  • Before: Telegram/Discord users saw a spurious "⚠️ Agent couldn't generate a response" bubble after the agent had already replied via the messaging tool.
  • After: the assistant reply stands alone; no extra warning when the turn ended cleanly.

Made with Cursor

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/agents/pi-embedded-runner/run.incomplete-turn.test.ts (modified, +46/-4)
  • src/agents/pi-embedded-runner/run/incomplete-turn.ts (modified, +11/-0)

Code Example

{
  "subsystem": "agent/embedded",
  "message": "incomplete turn detected: runId=a7615325-3fc6-45c3-a9d3-83c8be4944c5 sessionId=d0139187-8cec-43cb-afa9-953080687b3b stopReason=stop payloads=0 — surfacing error to user",
  "time": "2026-04-23T01:53:31.588+02:00"
}

---

{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23665","time":"2026-04-23T01:53:01.812+02:00"}
{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23666","time":"2026-04-23T01:53:19.782+02:00"}
{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23667","time":"2026-04-23T01:53:20.791+02:00"}
{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23668","time":"2026-04-23T01:53:25.146+02:00"}
{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23669","time":"2026-04-23T01:53:26.141+02:00"}

---

{
  "event": "embedded_run_failover_decision",
  "runId": "686c2cdc-b1f2-41a5-ae1f-351e1660734c",
  "stage": "assistant",
  "decision": "surface_error",
  "provider": "ollama",
  "model": "kimi-k2.6:cloud",
  "aborted": true,
  "time": "2026-04-23T01:55:53.670+02:00"
}
RAW_BUFFERClick to expand / collapse

Bug Report: "Incomplete Turn" False Positive — Messages Sent But Error Still Surfaced

Summary

OpenClaw detects an "incomplete turn" (stopReason=stop payloads=0) and surfaces the error "⚠️ Agent couldn't generate a response" to the user. The agent generated and attempted to send multiple response messages (confirmed by gateway logs showing telegram sendMessage ok), but the user never received them. The turn completion detection logic correctly flagged an empty assistant payload, but the underlying issue is that messages marked as "sent" by the gateway never reached the user.

This is distinct from other payloads=0 issues — the model DID produce output, and the gateway DID report successful sends, but Telegram never delivered the messages to the user.

Environment

  • OpenClaw Version: v2026.4.20 (115f05d)
  • OS: macOS 15.4.1 (Darwin 25.4.0 arm64)
  • Node.js: v25.8.1
  • Model in use: ollama/kimi-k2.6:cloud (primary), with fallbacks
  • Channel: Telegram (direct chat)

Observed Behavior

Timeline of Events

Time (UTC+2)EventEvidence
01:52:26User sends message: "ga daar (what happened)"Telegram message log
01:53:01Agent sends message #23665telegram sendMessage ok chat=2077431888 message=23665
01:53:19Agent sends message #23666telegram sendMessage ok chat=2077431888 message=23666
01:53:20Agent sends message #23667telegram sendMessage ok chat=2077431888 message=23667
01:53:25Agent sends message #23668telegram sendMessage ok chat=2077431888 message=23668
01:53:26Agent sends message #23669telegram sendMessage ok chat=2077431888 message=23669
01:53:31⚠️ OpenClaw detects "incomplete turn"incomplete turn detected: runId=a7615325... stopReason=stop payloads=0
01:53:31Error surfaced to user"⚠️ Agent couldn't generate a response"
01:53:31Error message #23670 senttelegram sendMessage ok chat=2077431888 message=23670

Key Observation

The gateway reported 5 messages as "sent" (23665-23669), but the user never received them. This suggests either:

  1. Telegram silently dropped the messages (rate limiting, content filtering)
  2. The gateway reported success before actual delivery confirmation
  3. The messages were sent to a different context/thread

Then OpenClaw flagged the turn as payloads=0 (because the final assistant payload was empty after tool calls) and showed an error — which was technically correct about the empty payload, but the real issue is the undelivered messages.

Relevant Log Excerpts

Agent/Embedded Subsystem

{
  "subsystem": "agent/embedded",
  "message": "incomplete turn detected: runId=a7615325-3fc6-45c3-a9d3-83c8be4944c5 sessionId=d0139187-8cec-43cb-afa9-953080687b3b stopReason=stop payloads=0 — surfacing error to user",
  "time": "2026-04-23T01:53:31.588+02:00"
}

Gateway/Telegram (showing successful sends BEFORE the error)

{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23665","time":"2026-04-23T01:53:01.812+02:00"}
{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23666","time":"2026-04-23T01:53:19.782+02:00"}
{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23667","time":"2026-04-23T01:53:20.791+02:00"}
{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23668","time":"2026-04-23T01:53:25.146+02:00"}
{"subsystem":"gateway/channels/telegram","message":"telegram sendMessage ok chat=2077431888 message=23669","time":"2026-04-23T01:53:26.141+02:00"}

Failover Decision (Second Run)

{
  "event": "embedded_run_failover_decision",
  "runId": "686c2cdc-b1f2-41a5-ae1f-351e1660734c",
  "stage": "assistant",
  "decision": "surface_error",
  "provider": "ollama",
  "model": "kimi-k2.6:cloud",
  "aborted": true,
  "time": "2026-04-23T01:55:53.670+02:00"
}

Analysis

What Appears to Happen

  1. Agent generates a long response (5+ Telegram messages)
  2. Model signals stopReason=stop (normal completion)
  3. OpenClaw turn completion logic counts payloads=0
  4. Despite messages already being sent, error is surfaced

Hypothesis: Async Message Sending vs Turn Tracking

The turn tracking may not account for async message delivery. If the model's response is split across multiple tool calls (e.g., multiple sendMessage calls to Telegram), the turn tracker might see payloads=0 because the "final" assistant payload is empty, even though intermediate messages were already delivered.

Contrast with Other payloads=0 Issues

  • #67575 / #68076: Model genuinely returns empty response → payloads=0 is correct
  • THIS ISSUE: Model returns content, sends it, THEN signals stop → payloads=0 is a false positive

Reproduction Steps

  1. Start a conversation that requires a long, multi-part response
  2. Ensure the agent uses multiple tool calls (e.g., multiple sendMessage calls)
  3. Observe that messages are delivered
  4. Check logs for incomplete turn detected with payloads=0
  5. User receives "⚠️ Agent couldn't generate a response" despite having received content

Suggested Fix

Option 1: Track In-Flight Messages

Track messages that have been sent via tool calls during the turn. If messages were successfully delivered, do not flag as payloads=0.

Option 2: Distinguish Empty Response from Tool-Only Response

A turn that only contains tool calls (no final assistant message) should not be flagged as incomplete if the tool calls succeeded.

Option 3: Post-Turn Validation

Before surfacing the error, check if any messages were actually sent to the user during the turn. If yes, suppress the error.

Workarounds

User-Level (Current):

  • Re-send the message
  • The second attempt usually succeeds (as seen: the follow-up "waarom werd het afgekapt?" got a proper response)

Agent-Level:

  • Keep responses shorter (fewer tool calls per turn)
  • Avoid sending multiple messages in rapid succession

Related Issues

  • #67575: OpenRouter responses received but not returned (different — genuine empty response)
  • #68076: Incomplete turn with exact payloads=0 error (different — no output at all)
  • #67425: Local embedded runner abandons turns with replayInvalid=true (different — abandoned state)

Cross-Reference Note

This may be related to how OpenClaw handles multi-message responses or tool-call-heavy turns. The payloads=0 check appears to only count the final assistant message payload, not intermediate tool outputs.

Additional Context

  • This occurred during a heartbeat configuration discussion, where the agent was checking config files and sending status updates
  • The model (kimi-k2.6:cloud) has been observed to have similar issues with long responses (see also: session lock issues with this model)
  • The error was not a model timeout — the model completed its reasoning and sent all messages

Attachments

  • Log excerpts showing successful message sends AND subsequent error
  • Timeline of events
  • Full session log (if available)
  • Gateway debug log at time of incident

Reported by: Johannes Huijbregts via Echo assistant Date: 2026-04-23 OpenClaw Version: v2026.4.20 (115f05d) Session: runId=a7615325-3fc6-45c3-a9d3-83c8be4944c5

extent analysis

TL;DR

Implement a mechanism to track in-flight messages and distinguish between empty responses and tool-only responses to prevent false positives for "incomplete turn" errors.

Guidance

  1. Review turn completion logic: Examine how OpenClaw currently handles turn completion, especially for multi-message responses and tool-call-heavy turns.
  2. Implement in-flight message tracking: Develop a system to track messages sent via tool calls during a turn, ensuring that successfully delivered messages are accounted for.
  3. Distinguish between response types: Modify the logic to differentiate between empty responses (where the model genuinely returns no content) and tool-only responses (where the model sends content via tool calls but has no final assistant message).

Example

// Pseudocode example of tracking in-flight messages
{
  "turn": {
    "id": "a7615325-3fc6-45c3-a9d3-83c8be4944c5",
    "messages": [
      { "id": 23665, "status": "sent" },
      { "id": 23666, "status": "sent" },
      //...
    ]
  }
}

Notes

  • This solution focuses on addressing the false positive "incomplete turn" errors by improving how OpenClaw tracks and handles multi-message responses and tool calls.
  • Further analysis may be required to fully understand the interaction between the model, tool calls, and turn completion logic.

Recommendation

Apply the workaround of tracking in-flight messages and distinguishing between response types, as this directly addresses the identified issue and prevents false positives for "incomplete turn" errors.

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]: Incomplete turn false positive — messages sent but error still surfaced (payloads=0 with output) [1 pull requests, 1 participants]