Skip to content

Sub-agent announce flow does not suppress NO_REPLY — leaks 'NO' + '✅ Done.' to channel #27531

@dario-github

Description

@dario-github

Bug Description

When a sub-agent replies NO_REPLY, the announce flow in subagent-announce.ts does not suppress it. This causes garbage messages like "NO" and "✅ Done." to leak into Slack channels.

Root Cause

In src/agents/subagent-announce.ts line ~1207, only isAnnounceSkip() is checked (which matches "ANNOUNCE_SKIP"). The SILENT_REPLY_TOKEN ("NO_REPLY") is not checked:

// line ~1207
if (isAnnounceSkip(reply)) {   // only matches "ANNOUNCE_SKIP"
  return true;
}
// NO_REPLY falls through here

Then buildCompletionDeliveryMessage() treats "NO_REPLY" as valid findings text (hasFindings = true because it is not empty and not "(no output)"), producing:

✅ Subagent xxx finished

NO_REPLY

This is sent via the gateway send method (direct path for expectsCompletionMessage=true), which calls deliverOutboundPayloads without any isSilentReplyText filtering.

When the direct path fails (e.g., device token mismatch), it falls back to the agent method, injecting a [System Message] into the main session. The main agent then generates short garbage replies like "NO" (a streaming truncation of NO_REPLY) and "✅ Done." which get delivered to the channel.

Why Cron Jobs Are Not Affected

The cron delivery dispatch (src/cron/isolated-agent/delivery-dispatch.ts ~line 271) has a separate SILENT_REPLY_TOKEN check before calling runSubagentAnnounceFlow:

if (synthesizedText.toUpperCase() === SILENT_REPLY_TOKEN.toUpperCase()) {
  return params.withRunSession({ status: "ok", delivered: true, ... });
}

This correctly catches NO_REPLY for cron jobs. Sub-agents lack this guard.

Suggested Fix

Add isSilentReplyText(reply) check alongside isAnnounceSkip(reply) in runSubagentAnnounceFlow:

if (isAnnounceSkip(reply)) {
  return true;
}
// Add this:
if (isSilentReplyText(reply, SILENT_REPLY_TOKEN)) {
  return true;
}

The isSilentReplyText function is already imported in the file (SILENT_REPLY_TOKEN from ../auto-reply/tokens.js).

Steps to Reproduce

  1. Spawn a sub-agent via sessions_spawn with a task that results in NO_REPLY
  2. Observe announce message delivered to the requester channel containing "NO_REPLY" as findings
  3. If direct delivery fails and falls back to agent method, main agent generates garbage replies ("NO", "✅ Done.")

Environment

  • OpenClaw v2026.2.22
  • Channel: Slack
  • Observed frequently when sub-agents have nothing to report

Screenshots

Messages appearing in Slack thread:

NO
✅ Done.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions