From fcaf3531baf1ac087542903958a8ae82be2314a9 Mon Sep 17 00:00:00 2001 From: Duc Nguyen <87276974+itsddvn@users.noreply.github.com> Date: Sat, 28 Feb 2026 11:10:18 +0700 Subject: [PATCH] fix(channels): start outbound dispatcher before channel check (#15) StartAll() returned early when no channels existed at boot, skipping the dispatchOutbound goroutine. Channels loaded later via Reload() assumed the dispatcher was running, causing outbound messages (agent responses) to never reach Telegram. Move dispatcher startup before the empty-channel early return so dynamically loaded channels always have a running consumer. Co-authored-by: ntduc --- internal/channels/manager.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/channels/manager.go b/internal/channels/manager.go index 7fed75e6..fa5ea520 100644 --- a/internal/channels/manager.go +++ b/internal/channels/manager.go @@ -45,10 +45,17 @@ func NewManager(msgBus *bus.MessageBus) *Manager { } // StartAll starts all registered channels and the outbound dispatch loop. +// The dispatcher is always started even when no channels exist yet, +// because channels may be loaded dynamically later via Reload(). func (m *Manager) StartAll(ctx context.Context) error { m.mu.Lock() defer m.mu.Unlock() + // Always start the outbound dispatcher — channels may be added later via Reload(). + dispatchCtx, cancel := context.WithCancel(ctx) + m.dispatchTask = &asyncTask{cancel: cancel} + go m.dispatchOutbound(dispatchCtx) + if len(m.channels) == 0 { slog.Warn("no channels enabled") return nil @@ -56,11 +63,6 @@ func (m *Manager) StartAll(ctx context.Context) error { slog.Info("starting all channels") - dispatchCtx, cancel := context.WithCancel(ctx) - m.dispatchTask = &asyncTask{cancel: cancel} - - go m.dispatchOutbound(dispatchCtx) - for name, channel := range m.channels { slog.Info("starting channel", "channel", name) if err := channel.Start(ctx); err != nil {