Skip to content

[Bug]: sessions_yield resume reply recorded but not delivered; auto-announce direct-text mirror delivered instead → user gets child raw summary, not parent reply #90944

Description

@lukemyself-hub

Bug type

Runtime / message delivery (data loss — user-visible message lost)

Beta release blocker

No

Summary

When a parent session calls sessions_yield to wait on a subagent spawned with mode: "run" and the default delivery.mode: "announce", two outputs are produced for the same child completion:

  1. The parent's yield-resume reply (a normal assistant turn, generated after the completion event) — committed to the transcript but never delivered to the external channel.
  2. The subagent auto-announce direct-text delivery (delivery-mirror, …:text-direct), which delivers the child's raw result textthis one reaches the user.

Net effect: the user receives the subagent's raw completion summary (e.g. "Sync completed successfully. Here's the summary: …") instead of the parent agent's intended, natural-voice reply. The intended reply is silently dropped from the channel even though it exists in the session transcript.

This is not the same as the duplicate-message failure mode in #78369 (there the user sees both); here the user sees only the announce and the parent's reply is lost. It is also not #81490 (the yielded session was not orphaned — it resumed in place; see evidence below).

Steps to reproduce

  1. From a parent session bound to an external channel (Feishu direct chat in this case), spawn a subagent with mode: "run" and the default delivery mode (announce / agentTurn).
  2. Immediately call sessions_yield to wait for the child's completion event.
  3. Let the child complete with a non-empty text result.
  4. On resume, the parent generates a reply that depends on the child's output.
  5. Observe the external channel: the user receives the child's raw result text (the auto-announce direct-text mirror), not the parent's resume reply.

Repro probability: intermittent. In the observed case the two transcript commits were ~672 ms apart, suggesting a timing-sensitive race between the announce direct-text delivery and the yield-resume reply delivery.

Expected behavior

Exactly one user-visible message per completion, and it should be the parent's intended resume reply. The auto-announce direct-text path should not deliver the child's raw summary when the parent has produced (or is about to produce) its own reply for the same completion.

Actual behavior

The parent's resume reply is written to the transcript but not delivered to the channel; the auto-announce direct-text mirror (the child's raw result) is delivered instead.

Evidence (verified from the parent session transcript)

Session: agent:main:feishu:direct:ou_24dea12546e7358d53bac7a00d32e8e1
Child: agent:main:subagent:09d6d986-a4fe-4fea-84f3-3471ef0dbec6 (taskName: diary-feishu-sync, runId: 9e662f80-99d6-40fc-b1dc-63f82b115c13)
Spawn: mode: "run", default delivery.mode: announce, channel = Feishu direct (websocket).

Transcript order (parent session .jsonl):

line type / role model content delivered to user?
54 assistant deepseek-v4-pro "好的,本地日记已经写好了 ✅ 正在同步到飞书云文档,稍等——" (pre-yield) ✅ yes
55 toolResult sessions_yield{"status":"yielded","message":"等待飞书同步完成"}
56 custom_message customType: openclaw.sessions_yield (the yield marker)
57 assistant deepseek-v4-pro "飞书也同步好了 ✅ …周末先放一放 🦞" — the parent's resume reply, parentId = the yield marker (56) no
58 assistant delivery-mirror "Sync completed successfully. Here's the summary: …" — idempotencyKey: announce:v1:agent:main:subagent:09d6d986-…:9e662f80-…:text-direct ✅ yes

The user then confirmed in-channel (subsequent turns in the same transcript) that the last message they saw was "Sync completed successfully. Here's the summary: …" (line 58), and that the agent's reply (line 57) never arrived.

Key points from the raw records:

  • Line 57 is a genuine parent assistant turn (provider: deepseek, model: deepseek-v4-pro, real token usage, stopReason: stop) whose parentId is the openclaw.sessions_yield marker — i.e. the yielded session resumed in place and produced this reply (so this is not the orphaned-session case of [Bug]: subagent completion spawns a fresh run on the parent's route instead of resuming the yielded session (supersedes #80310) #81490).
  • Line 58 has model: delivery-mirror, zero token usage, and the …:text-direct idempotency key — i.e. it is the auto-announce direct-text delivery of the child's raw result, not a parent LLM turn.
  • The two were committed ~672 ms apart (line 57 envelope …:37.244Z, line 58 envelope …:37.916Z).

Note: line 57 shows a ~13 s gap between its inner model timestamp and its transcript-commit timestamp. This is not anomalous — other replies the user did receive (later turns in the same session) show the same ~12 s gap. It is an artifact of the record format (inner = model completion, envelope = post-delivery commit), so it is not evidence of a stall. Mentioned here only to preempt that lead.

Likely mechanism (hypothesis — not fully pinned)

For the same child completion there appear to be two uncoordinated delivery producers:

  • The yield-resume path, which resumes the parent and produces a user-facing reply (line 57).
  • The subagent auto-announce path, which (for the announce default) ends up taking the direct-text branch and delivers the child's raw result (line 58) via sendMessage with a …:text-direct idempotency key + mirror.

The parent's resume reply is committed to the transcript but does not reach the channel, while the announce direct-text delivery does. The two are not deduplicated against each other for outbound delivery.

Relevant source paths (recovered from the //#region src/... markers in the published dist):

  • src/agents/subagent-announce-dispatch.tsrunSubagentAnnounceDispatch (the steer-vs-direct decision; when expectsCompletionMessage is true it runs direct() first).
  • src/agents/subagent-announce-delivery.tsdeliverTextCompletionDirect (produces the …:text-direct mirror using resolveTextCompletionDirectFallback, i.e. the child's result), maybeSteerSubagentAnnounce, runAnnounceAgentCall (note forceSyntheticClient for announce-mediated runs), sendSubagentAnnounceDirectly.
  • src/agents/announce-idempotency.tsbuildAnnounceIdempotencyKey / buildAnnounceIdFromChildRun (the announce:v1:<childSessionKey>:<childRunId> key seen on line 58).
  • src/auto-reply/reply/completion-delivery-policy.tscompletionRequiresMessageToolDelivery.

The open question for maintainers: when a yielded parent resumes and produces its own reply for a completion, why is that reply's outbound delivery suppressed while the announce direct-text path still delivers the child's raw result? A structural dedup between the yield-resume reply and the announce delivery (cf. the fingerprint/short-circuit proposal in #78369) would prevent both the duplicate case (#78369) and this lost-reply case.

Related issues

OpenClaw version

2026.6.1 (2e08f0f)

Operating system

macOS 26 (Darwin 25.5.0, arm64), Node v22.22.3. Channel: Feishu, connectionMode: websocket.

Additional information

  • Subagent/delivery config was all defaults (no subagents/announce/delivery overrides in openclaw.json).
  • The spawn tool result itself carries guidance to not poll and to "wait for runtime completion events to arrive as user messages"; sessions_yield is an event-wait (not polling), so this appears to be a supported usage pattern rather than misuse.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:message-lossChannel message delivery can be lost, duplicated, or misrouted.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions