sipeed/picoclaw · error

channel manager not configured

Error message

channel manager not configured

What it means

Returned by the reaction tool's callback when the AgentLoop has no channels.Manager wired in. picoclaw injects the manager after construction via al.SetChannelManager (pkg/agent/agent_inject.go:22), but the reaction tool is registered from config regardless, so the callback guards on al.channelManager == nil. If this fires, the 'reaction' tool was enabled but the embedding process never attached a channel manager.

Source

Thrown at pkg/agent/agent_init.go:225

					SessionKey:       outboundSessionKey,
					Scope:            outboundScope,
					Content:          content,
					ReplyToMessageID: replyToMessageID,
				}
				if al.channelManager != nil && channel != "" {
					return al.channelManager.SendMessage(ctx, outboundMessage)
				}
				pubCtx, pubCancel := context.WithTimeout(context.Background(), 5*time.Second)
				defer pubCancel()
				return msgBus.PublishOutbound(pubCtx, outboundMessage)
			})
			agent.Tools.Register(messageTool)
		}
		if cfg.Tools.IsToolEnabled("reaction") {
			reactionTool := tools.NewReactionTool()
			reactionTool.SetReactionCallback(func(ctx context.Context, channel, chatID, messageID string) error {
				if al.channelManager == nil {
					return fmt.Errorf("channel manager not configured")
				}
				ch, ok := al.channelManager.GetChannel(channel)
				if !ok {
					return fmt.Errorf("channel %s not found", channel)
				}
				rc, ok := ch.(channels.ReactionCapable)
				if !ok {
					return fmt.Errorf("channel %s does not support reactions", channel)
				}
				_, err := rc.ReactToMessage(ctx, chatID, messageID)
				return err
			})
			agent.Tools.Register(reactionTool)
		}

		// Send file tool (outbound media via MediaStore — store injected later by SetMediaStore)
		if cfg.Tools.IsToolEnabled("send_file") {
			sendFileTool := tools.NewSendFileTool(

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Create and start a channels.Manager (channels.NewManager + RegisterChannel + StartAll) and pass it via al.SetChannelManager(cm) before al.Run(ctx)
  2. If channels are intentionally unused, remove 'reaction' from the enabled tools in config so the tool is never registered
  3. Fix startup ordering: build manager -> register/start channels -> SetChannelManager -> Run
  4. Add a startup smoke test that dispatches a reaction to a scratch channel and asserts no error

Example fix

// before
al := agent.NewAgentLoop(cfg, ...)
go al.Run(ctx) // reaction tool enabled in cfg, no channel manager

// after
al := agent.NewAgentLoop(cfg, ...)
cm, err := channels.NewManager(cfg, msgBus, mediaStore)
if err != nil {
    return err
}
cm.RegisterChannel("telegram", telegramChannel)
if err := cm.StartAll(ctx); err != nil {
    return err
}
al.SetChannelManager(cm)
go al.Run(ctx)
Defensive patterns

Strategy: validation

Validate before calling

cm, err := channels.NewManager(cfg, msgBus, mediaStore)
if err != nil {
    return fmt.Errorf("create channel manager: %w", err)
}
if err := cm.StartAll(ctx); err != nil {
    return fmt.Errorf("start channels: %w", err)
}
al.SetChannelManager(cm) // must happen before al.Run when channel tools are enabled
if cfg.Tools.IsToolEnabled("reaction") || cfg.Tools.IsToolEnabled("message") {
    if cm == nil {
        return fmt.Errorf("channel-bound tools enabled but no channel manager wired")
    }
}

Prevention

When it happens

Trigger: Config enables the 'reaction' tool (cfg.Tools.IsToolEnabled("reaction")) and the model invokes it while SetChannelManager was never called (or called with nil) — e.g. running the loop headless with only the message bus, or starting dispatch before channel setup finished.

Common situations: Embedding picoclaw in direct/CLI mode without channels but keeping the default tool list; tests constructing an AgentLoop by hand and forgetting the inject calls; startup-ordering bugs where al.Run starts before channels are registered.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/c20abc30bfe2631c. Report an issue: GitHub.