Bug type
Race condition / announce result extraction
Beta release blocker
No
Summary
When a subagent's final turn includes a large tool result (>16KB, e.g. a curl command fetching OpenCode session JSON), the subagent_announce mechanism delivers the toolResult content to the parent session instead of the subagent's final assistant text (the structured completion report).
The parent agent receives a truncated toolResult JSON and never sees the subagent's actual completion summary.
Steps to reproduce
- Spawn a subagent (e.g.
devops-agent) that performs multiple tool calls in its final turn.
- The subagent's last tool call fetches large output (e.g.
curl retrieving a 96KB OpenCode session JSON via exec).
- The subagent then outputs a short structured report as its final assistant text (e.g. 1KB summary with test results and deliverables).
- When the subagent completes, the parent receives a
subagent_announce containing the truncated curl output, not the assistant report.
Expected behavior
The announce should deliver the subagent's final assistant text (the completion report), not the preceding toolResult.
Per the documented strategy: "Result — latest visible assistant reply text, otherwise sanitized latest tool/toolResult text" — the assistant text should take priority.
Actual behavior
The announce delivers the toolResult from the last tool call:
- The 96KB curl output becomes the announce result
- It gets truncated at the 16KB
DEFAULT_MAX_LIVE_TOOL_RESULT_CHARS limit
- The actual assistant report (1KB structured summary) is never delivered to the parent
Observed comparison
| Subagent |
Last tool output size |
Parent receives |
Correct? |
| BA (small toolResult) |
~100 bytes |
Assistant report ✅ |
Yes |
| DevOps speed test (small toolResult) |
~500 bytes |
Assistant report ✅ |
Yes |
| DevOps formal task (96KB curl) |
~96KB |
Truncated toolResult ❌ |
No |
Root cause analysis
In subagent-session-cleanup-BCiiUQZB.js, readSubagentOutput() calls chat.history to read the child session messages, then summarizeSubagentOutputHistory() walks the message list to extract:
latestSilentText (NO_REPLY)
latestAssistantText (assistant text)
- Partial progress (timeout fallback)
latestRawText (includes toolResult — final fallback)
The issue appears to be a race condition: when the framework calls chat.history immediately after the subagent completes, the final assistant message may not yet be fully persisted to the transcript. The API returns the large toolResult as the latest visible content, and selectSubagentOutputText falls through to latestRawText.
The readLatestSubagentOutputWithRetry fallback also fails to recover the final assistant text.
Small toolResults don't trigger visible issues because even when the race occurs, the toolResult content fits within the 16KB limit and the parent can still see the full context. The problem only manifests when toolResults are large enough to be truncated.
Environment
- OpenClaw version: 2026.5.x
- Reproduction: Any subagent whose final turn includes a tool call producing >16KB output
- Framework files:
subagent-session-cleanup-BCiiUQZB.js (extract logic), compaction-BxhK_yKO.js (16KB truncation)
Suggested fix
- Increase retry delay/window in
readLatestSubagentOutputWithRetry to wait for final assistant text to persist before falling back to toolResult.
- Or: After
chat.history returns, check if the last assistant message has tool calls without a subsequent assistant text reply — if so, delay and retry before using latestRawText.
- Or: Add a small grace period (e.g. 500ms–1s) between subagent completion and announce delivery to allow transcript persistence.
Bug type
Race condition / announce result extraction
Beta release blocker
No
Summary
When a subagent's final turn includes a large tool result (>16KB, e.g. a
curlcommand fetching OpenCode session JSON), thesubagent_announcemechanism delivers the toolResult content to the parent session instead of the subagent's final assistant text (the structured completion report).The parent agent receives a truncated toolResult JSON and never sees the subagent's actual completion summary.
Steps to reproduce
devops-agent) that performs multiple tool calls in its final turn.curlretrieving a 96KB OpenCode session JSON viaexec).subagent_announcecontaining the truncated curl output, not the assistant report.Expected behavior
The announce should deliver the subagent's final assistant text (the completion report), not the preceding toolResult.
Per the documented strategy: "Result — latest visible assistant reply text, otherwise sanitized latest tool/toolResult text" — the assistant text should take priority.
Actual behavior
The announce delivers the toolResult from the last tool call:
DEFAULT_MAX_LIVE_TOOL_RESULT_CHARSlimitObserved comparison
Root cause analysis
In
subagent-session-cleanup-BCiiUQZB.js,readSubagentOutput()callschat.historyto read the child session messages, thensummarizeSubagentOutputHistory()walks the message list to extract:latestSilentText(NO_REPLY)latestAssistantText(assistant text)latestRawText(includes toolResult — final fallback)The issue appears to be a race condition: when the framework calls
chat.historyimmediately after the subagent completes, the final assistant message may not yet be fully persisted to the transcript. The API returns the large toolResult as the latest visible content, andselectSubagentOutputTextfalls through tolatestRawText.The
readLatestSubagentOutputWithRetryfallback also fails to recover the final assistant text.Small toolResults don't trigger visible issues because even when the race occurs, the toolResult content fits within the 16KB limit and the parent can still see the full context. The problem only manifests when toolResults are large enough to be truncated.
Environment
subagent-session-cleanup-BCiiUQZB.js(extract logic),compaction-BxhK_yKO.js(16KB truncation)Suggested fix
readLatestSubagentOutputWithRetryto wait for final assistant text to persist before falling back to toolResult.chat.historyreturns, check if the last assistant message has tool calls without a subsequent assistant text reply — if so, delay and retry before usinglatestRawText.