Skip to content

Subagent announce delivery fails with 'Outbound not configured for channel' on Telegram (and likely other channels) #56347

@viktorsolost

Description

@viktorsolost

Bug Description

Subagent completion announcements fail to deliver to Telegram with:

Error: Outbound not configured for channel: telegram

The announce flow retries 3 times (each with a different ephemeral WS connection) and gives up. Normal replies to Telegram work fine.

Root Cause

Plugin registry mismatch between pinned and active registries.

In src/channels/plugins/outbound/load.ts, createChannelRegistryLoader uses getActivePluginRegistry() to resolve channel outbound adapters:

const loadOutboundAdapterFromRegistry = createChannelRegistryLoader(
  (entry) => entry.plugin.outbound
);

function createChannelRegistryLoader(resolveValue) {
  // ...
  return async (id) => {
    const registry = getActivePluginRegistry();  // ← uses unpinned registry
    // ...
  };
}

However, resolveOutboundChannelPlugin (which runs just before in createChannelHandler) uses getChannelPlugin(), which resolves through requireActivePluginChannelRegistry() — the pinned registry.

The gateway pins the channel registry at startup via pinActivePluginChannelRegistry() so that config-schema reads, agent-specific plugin loads, and other setActivePluginRegistry() calls cannot evict channel plugins. This pinning is correct and intentional.

The problem: when the announce delivery path runs after agent completion, the active registry may have been swapped to an agent-specific or ephemeral registry that does not include the Telegram (or other channel) outbound adapter. The pinned registry still has it, but loadOutboundAdapterFromRegistry does not consult the pinned registry.

Normal replies work because they execute within the gateway's main request handling path where the active registry happens to be in sync with the pinned one.

Steps to Reproduce

  1. Configure Telegram (or any external channel)
  2. Send a message from Telegram that triggers a subagent spawn
  3. Wait for the subagent to complete
  4. Observe: announce delivery fails with Outbound not configured for channel: telegram
  5. Observe: normal direct replies to Telegram still work fine

Log Evidence

[ws] ⇄ res ✗ agent errorCode=UNAVAILABLE errorMessage=Error: Outbound not configured for channel: telegram conn=aeea7ac2…90a4
[ws] ⇄ res ✗ agent cached=true conn=22f5fcd7…9a9b    ← different connection
[ws] ⇄ res ✗ agent cached=true conn=8613cde9…6127    ← different connection
[warn] Subagent announce give up (retry-limit) retries=3

Each retry uses a different ephemeral WS connection, confirming the announce flow creates fresh connections that all fail to find the adapter.

Meanwhile, delivery-recovery on gateway restart successfully delivers to Telegram (because it runs within the gateway process where the pinned registry is available).

Proposed Fix

In createChannelRegistryLoader, use the pinned channel registry instead of the general active registry:

- const registry = getActivePluginRegistry();
+ const registry = requireActivePluginChannelRegistry();

Or alternatively, have loadChannelOutboundAdapter call through getChannelPlugin (which already uses the pinned registry) and extract the outbound adapter from there.

Environment

  • OpenClaw version: latest (same build confirmed working on another instance with different usage patterns)
  • Channel: Telegram
  • Clean gateway restart does not fix the issue (confirmed)
  • Affects: subagent announce delivery, cron job announce delivery (any deliverOutboundPayloads path triggered from agent completion)

Labels

bug

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