You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
empty-error-retry in runEmbeddedAgent only fires when the failed turn produced zero output tokens and zero content blocks, so an upstream stop_reason: "error" that returns with a thinking block + non-zero usage.output silently terminates a multi-step turn with no retry, no fallback engagement, and no user-visible error in the channel.
Steps to reproduce
Run OpenClaw 2026.5.28 on top of an Anthropic-style provider that occasionally returns stop_reason: "error" mid-turn (observed against vertexai/claude-opus-4-8).
Issue a multi-step prompt that triggers tool use before the final assistant message (e.g. "search the web, fetch 2 pages, then create 5 group threads and @ two people").
Wait until the upstream provider returns stop_reason: "error" during the assistant turn that follows the read-only tool work. With a reasoning-enabled model, the streamed response will typically arrive with content: [{ type: "thinking", ... }] and usage.output > 0 before the gateway closes the stream with stop_reason: "error".
Observe that:
anthropic-BEgJnt4r.js:425 (and equivalent in src/providers/anthropic) throws An unknown error occurred.
MAX_EMPTY_ERROR_RETRIES retry path at src/agents/embedded-agent-runner/run.ts:3454-3473 does not fire because (usage.output ?? 0) === 0 and (silentErrorContent?.length ?? 0) === 0 are both false.
The run returns with meta.livenessState === "blocked" and an isError: true incomplete-turn payload, but on the cron/isolated-session delivery path used here, that payload is not surfaced to the originating chat channel.
Expected behavior
A turn-level upstream provider error (stop_reason: "error") on a multi-step task should either:
Be retried by empty-error-retry (as today's logic already does for output === 0 && content === []), since neither a thinking-only content nor a non-zero usage.output represents user-visible progress; or
At minimum, surface the ⚠️ Agent couldn't generate a response. Please try again. payload to the originating channel/thread so the user knows the turn died, instead of completing the session with no visible output.
In 2026.4.x series the same upstream behaviour also produced a silent end, so this is not a regression — but the existing empty-error-retry net was designed to catch exactly this failure shape and its current guards leak the most common reasoning-model variant of it.
Actual behavior
The run completes with livenessState: "blocked" and an isError: true incomplete-turn payload, but:
No retry attempt is logged ([empty-error-retry] line never appears).
No model.fallback_step event is emitted.
The user-visible group/thread receives nothing — neither the incomplete-turn warning string nor any other status. The originating session is marked ended.
Multi-step tasks like "search → fetch → create N threads → @ users" therefore silently abort midway with no observable failure signal on the chat surface.
OpenClaw version
2026.5.28 (also confirmed on main at bb6e47729c — guards unchanged)
Operating system
macOS 15 (Darwin 25.5.0 arm64)
Install method
npm global (/opt/homebrew/lib/node_modules/openclaw)
Model
vertexai/claude-opus-4-8 (reasoning-enabled). Behaviour is provider-agnostic: any Anthropic-style stream that ends with stop_reason: "error" after emitting a thinking block or any token of output will exhibit it.
Provider / routing chain
openclaw -> vertex-ai (claude-opus-4-8)
Additional provider/model setup details
Reasoning mode enabled on the agent so the assistant turn that crashed produced a single thinking content block before the stream errored.
Logs, screenshots, and evidence
Source references (relative to repo root at bb6e47729c):
src/agents/embedded-agent-runner/run.ts:3454-3473 — the actual guard:
Comments at run.ts:3437-3452 document the intent ("Content-empty guard: a reasoning-only error is a distinct failure mode handled elsewhere; only retry when the assistant truly produced nothing"), but in practice the "elsewhere" path (resolveReasoningOnlyRetryInstruction in src/agents/embedded-agent-runner/run/incomplete-turn.ts) explicitly returns null when assistant?.stopReason === "error":
src/agents/embedded-agent-runner/run/incomplete-turn.ts → resolveReasoningOnlyRetryInstruction (~line where it checks if (assistant?.stopReason === "error") return null;).
So the reasoning-only retry skips errored turns, and empty-error-retry skips turns whose content is a single thinking block + non-zero usage.output. The stop_reason=error + thinking-only outcome therefore falls into a gap between the two retry resolvers and reaches the terminal incomplete-turn detected branch.
Observed assistant turn fields from a real failure (vertexai/claude-opus-4-8):
Affected: any agent run that uses a reasoning-enabled Anthropic-style model and performs multi-step tool work (search → fetch → write/create), on any channel that delivers via the cron / isolated-session path.
Severity: High when it hits. Multi-step user tasks silently abort partway through with no error surfaced to the channel; user assumes the agent is still working and only notices when nothing arrives.
Frequency: Intermittent. Tied to upstream provider stability — every stop_reason: "error" on a reasoning-model mid-turn reproduces it.
Consequence: Lost work for the user (downstream steps never run), no failure signal in the chat surface, and the configured model.fallbacks chain never engages so there is no automated recovery.
Additional information
Suggested fix direction (not a PR yet, filing for maintainer triage):
Loosen the empty-error-retry guards so a stop_reason === "error" turn is retried when the only content blocks are thinking / redacted_thinking and there is no visible text emitted, regardless of usage.output. A thinking-only block carries no user-visible product, so resubmission cannot duplicate user-visible output.
Keep the existing hadPotentialSideEffects short-circuit so mutating tool calls / committed delivery still block resubmission.
When all retries exhaust on a stop_reason: "error" outcome, ensure the incompleteTurnText payload is delivered through the originating channel adapter (or at least logged with livenessState: "blocked" in a way the cron / isolated-session surfaces propagate to chat), so the failure is not silent at the user-visible layer.
Happy to send a PR once a maintainer confirms the preferred shape of fix.
Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
empty-error-retryinrunEmbeddedAgentonly fires when the failed turn produced zero output tokens and zero content blocks, so an upstreamstop_reason: "error"that returns with athinkingblock + non-zerousage.outputsilently terminates a multi-step turn with no retry, no fallback engagement, and no user-visible error in the channel.Steps to reproduce
2026.5.28on top of an Anthropic-style provider that occasionally returnsstop_reason: "error"mid-turn (observed againstvertexai/claude-opus-4-8).stop_reason: "error"during the assistant turn that follows the read-only tool work. With a reasoning-enabled model, the streamed response will typically arrive withcontent: [{ type: "thinking", ... }]andusage.output > 0before the gateway closes the stream withstop_reason: "error".anthropic-BEgJnt4r.js:425(and equivalent insrc/providers/anthropic) throwsAn unknown error occurred.stopReason: "error",errorMessage: "An unknown error occurred",aborted: false,timedOut: false,content.length === 1(singlethinkingblock),usage.output === 1120.MAX_EMPTY_ERROR_RETRIESretry path atsrc/agents/embedded-agent-runner/run.ts:3454-3473does not fire because(usage.output ?? 0) === 0and(silentErrorContent?.length ?? 0) === 0are both false.meta.livenessState === "blocked"and anisError: trueincomplete-turn payload, but on the cron/isolated-session delivery path used here, that payload is not surfaced to the originating chat channel.Expected behavior
A turn-level upstream provider error (
stop_reason: "error") on a multi-step task should either:empty-error-retry(as today's logic already does foroutput === 0 && content === []), since neither athinking-only content nor a non-zerousage.outputrepresents user-visible progress; ormodel.fallbackschain so the user-facing agent stays available (related: model.fallbacks chain does not engage on reasoning-only / empty-visible-reply failures #85422); or⚠️ Agent couldn't generate a response. Please try again.payload to the originating channel/thread so the user knows the turn died, instead of completing the session with no visible output.In
2026.4.xseries the same upstream behaviour also produced a silent end, so this is not a regression — but the existingempty-error-retrynet was designed to catch exactly this failure shape and its current guards leak the most common reasoning-model variant of it.Actual behavior
The run completes with
livenessState: "blocked"and anisError: trueincomplete-turn payload, but:[empty-error-retry]line never appears).model.fallback_stepevent is emitted.Multi-step tasks like "search → fetch → create N threads → @ users" therefore silently abort midway with no observable failure signal on the chat surface.
OpenClaw version
2026.5.28 (also confirmed on
mainatbb6e47729c— guards unchanged)Operating system
macOS 15 (Darwin 25.5.0 arm64)
Install method
npm global (
/opt/homebrew/lib/node_modules/openclaw)Model
vertexai/claude-opus-4-8 (reasoning-enabled). Behaviour is provider-agnostic: any Anthropic-style stream that ends with
stop_reason: "error"after emitting athinkingblock or any token of output will exhibit it.Provider / routing chain
openclaw -> vertex-ai (claude-opus-4-8)
Additional provider/model setup details
Reasoning mode enabled on the agent so the assistant turn that crashed produced a single
thinkingcontent block before the stream errored.Logs, screenshots, and evidence
Source references (relative to repo root at
bb6e47729c):src/agents/embedded-agent-runner/run.ts:3454-3473— the actual guard:Comments at
run.ts:3437-3452document the intent ("Content-empty guard: a reasoning-only error is a distinct failure mode handled elsewhere; only retry when the assistant truly produced nothing"), but in practice the "elsewhere" path (resolveReasoningOnlyRetryInstructioninsrc/agents/embedded-agent-runner/run/incomplete-turn.ts) explicitly returnsnullwhenassistant?.stopReason === "error":src/agents/embedded-agent-runner/run/incomplete-turn.ts→resolveReasoningOnlyRetryInstruction(~line where it checksif (assistant?.stopReason === "error") return null;).So the reasoning-only retry skips errored turns, and
empty-error-retryskips turns whose content is a singlethinkingblock + non-zerousage.output. Thestop_reason=error+thinking-only outcome therefore falls into a gap between the two retry resolvers and reaches the terminalincomplete-turn detectedbranch.Observed assistant turn fields from a real failure (vertexai/claude-opus-4-8):
Existing related issues for context (none cover this specific empty-error-retry gap):
model.fallbackschain does not engage on reasoning-only / empty-visible-reply failures (covers the sibling fallback gap).Impact and severity
stop_reason: "error"on a reasoning-model mid-turn reproduces it.model.fallbackschain never engages so there is no automated recovery.Additional information
Suggested fix direction (not a PR yet, filing for maintainer triage):
empty-error-retryguards so astop_reason === "error"turn is retried when the only content blocks arethinking/redacted_thinkingand there is no visible text emitted, regardless ofusage.output. Athinking-only block carries no user-visible product, so resubmission cannot duplicate user-visible output.hadPotentialSideEffectsshort-circuit so mutating tool calls / committed delivery still block resubmission.stop_reason: "error"outcome, ensure theincompleteTurnTextpayload is delivered through the originating channel adapter (or at least logged withlivenessState: "blocked"in a way the cron / isolated-session surfaces propagate to chat), so the failure is not silent at the user-visible layer.Happy to send a PR once a maintainer confirms the preferred shape of fix.