Skip to content

[Bug]: Subagent announce 3x duplicate still reproduces on 2026.6.1 release + Telegram — #89812 only fixes outbound/deliver path, not subagent-announce-delivery #91527

Description

@zackchiutw

Summary

#89995 was closed as "already implemented" because PR #89812 (commit 79896a24) wraps the post-delivery transcript mirror in try/catch inside deliverOutboundPayloadsCore. That fix is real, but it does not cover the subagent-announce delivery path. The same EmbeddedPromptLockFileChangedError: session file changed while embedded prompt lock was released still triggers 3× duplicate Telegram sends on the 2026.6.1 stable release (not just the 2026.6.1-beta.3 reported in #89995).

Environment

OpenClaw 2026.6.1 release (npm [email protected])
Channel Telegram (direct DM, accountId=writer, chatId=51406718)
OS Linux 6.17.0-1024-oem (Ubuntu)
Node v22.22.2
Spawn pattern sessions_spawn(taskName=..., mode="run") + sessions_yield
Reproducible 100% on this setup (1/1 observed so far)

Steps to reproduce

  1. From a Telegram DM parent session, call sessions_spawn(taskName="…", mode="run") with a long-running subagent task
  2. Parent calls sessions_yield to wait for the auto-announce
  3. The parent session is concurrently being written by the parent's own turn maintenance (the deferred turn-maintenance lane queues around the same time as the subagent's completion event)
  4. Subagent completes; gateway tries to inject the final text into the parent session
  5. Inject throws EmbeddedPromptLockFileChangedError because the parent jsonl changed while the embedded prompt lock was released
  6. Gateway's announce retry mechanism (in subagent-registry-dq-LC-2-.js:1037, maxAnnounceRetryCount: 3) fires 3 times with exponential backoff
  7. Each retry independently triggers a Telegram outbound send → user receives 3 identical messages

Why PR #89812 is not sufficient

PR #89812 only modified:

  • src/infra/outbound/deliver.ts (+24, -8)
  • src/infra/outbound/deliver.test.ts (+62, -1)

The fix wraps appendAssistantMessageToSessionTranscript(...) in try/catch so a mirror failure no longer fails the overall deliverOutboundPayloadsCore call. That stops the retry-on-mirror-failure path for the general outbound layer (matrix/telegram/etc. when an agent's reply is being sent to the user).

The subagent-announce path is a separate delivery module:

  • src/agents/subagent-announce-delivery.ts (runtime subagent-announce-delivery-DhVEJzi6.js)
  • src/agents/subagent-registry-lifecycle.ts (runtime subagent-registry-dq-LC-2-.js, retry orchestration)

That path still classifies the lock-change error as retriable and has no idempotency check between the in-band delivery attempt and the external outbound send. So the bug reproduced on the 2026.6.1 stable release, not just on beta.

Evidence (local journalctl excerpt)

07:24:00.400 [MiniMax-M3] (subagent) ✅ 任務完成 …(child session output)…
07:24:22.754 [agent/embedded] [context-engine] deferred turn maintenance queued
       lane=context-engine-turn-maintenance:agent:writer:telegram:direct:51406718
07:24:23.899 [telegram] outbound send ok accountId=writer chatId=51406718 messageId=2624
07:24:23.978 [warn] Subagent completion direct announce failed for run b434f673-…:
       text completion direct delivery failed:
       session file changed while embedded prompt lock was released:
       /home/zackchiu/.openclaw/agents/writer/sessions/6512e51b-….jsonl
07:24:25.716 [telegram] outbound send ok messageId=2625
07:24:25.731 [warn] Subagent completion direct announce failed … (retry 2)
07:24:28.500 [telegram] outbound send ok messageId=2626
07:24:28.517 [warn] Subagent completion direct announce failed … (retry 3)
07:24:28.700 [warn] Subagent announce give up (retry-limit) retries=3 endedAgo=29s

User report from the parent: "你摘要網頁又連發三次一樣的訊息了" (2026-06-09 07:39 local).

Related issues / PRs

Suggested fix (mirroring #89812's pattern)

Either:

  1. Add EmbeddedPromptLockFileChangedError / "session file changed while embedded prompt lock was released" to PERMANENT_ANNOUNCE_DELIVERY_ERROR_PATTERNS in the announce-delivery module, so the lock race stops triggering retries at all.

  2. Or add an idempotency token check before each announce retry, mirroring what deliverOutboundPayloadsCore now does: if the outbound send already succeeded in a prior attempt, the retry must not re-send. A lastDeliveredOutboundAt / outboundDeliveryId field on the announce entry, set when [telegram] outbound send ok is logged, would do it.

Option 1 is the smaller, safer change and matches the suggestion in the original #89995 report.

Workaround (in our setup, until this lands)

agents.defaults.subagents.announceTimeoutMs: 0 and agent-side deduplication on (childRunId, 60s) for incoming completion events. Not great — the proper fix is upstream.

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