sipeed/picoclaw · error
channel %q: config not found
Error message
channel %q: config not found
What it means
RegisterSafeFactory installs a type-safe wrapper around per-channel constructors; when the manager looks up cfg.Channels[channelName] to build a channel, a missing map entry yields this error. It means the registry was asked to construct a channel whose name has no corresponding stanza under channels in the loaded config.Config — the factory ran, but there is nothing to decode settings from.
Source
Thrown at pkg/channels/registry.go:47
// RegisterSafeFactory is a convenience wrapper that handles GetDecoded() error checking
// and type assertion, reducing boilerplate in channel init() functions.
//
// Usage:
//
// func init() {
// channels.RegisterSafeFactory(config.ChannelTelegram,
// func(bc *config.Channel, c *config.TelegramSettings, b *bus.MessageBus) (channels.Channel, error) {
// return NewTelegramChannel(bc, c, b)
// })
// }
func RegisterSafeFactory[S any](
channelType string,
ctor func(bc *config.Channel, settings *S, bus *bus.MessageBus) (Channel, error),
) {
RegisterFactory(channelType, func(channelName, _ string, cfg *config.Config, b *bus.MessageBus) (Channel, error) {
bc := cfg.Channels[channelName]
if bc == nil {
return nil, fmt.Errorf("channel %q: config not found", channelName)
}
decoded, err := bc.GetDecoded()
if err != nil {
return nil, fmt.Errorf("channel %q: failed to decode settings: %w", channelName, err)
}
settings, ok := decoded.(*S)
if !ok {
return nil, fmt.Errorf("channel %q: expected %T settings, got %T", channelName, (*S)(nil), decoded)
}
return ctor(bc, settings, b)
})
}
// getFactory looks up a channel factory by name.
func getFactory(name string) (ChannelFactory, bool) {
factoriesMu.RLock()
defer factoriesMu.RUnlock()
f, ok := factories[name]View on GitHub (pinned to 49183d7e8d)
Solutions
- Add a channels.<channelName> stanza with the correct key exactly matching the requested name (keys are case-sensitive).
- Fix the mismatch: whichever side names the channel (enabled list, manager, CLI) must equal the map key in cfg.Channels.
- Validate config at startup — iterate the names you intend to enable and assert each has a cfg.Channels entry before constructing channels.
- Check YAML indentation so each channel sits directly under channels:.
Example fix
# before: enabled name has no matching stanza
channels:
telegram-main:
type: telegram
...
# manager asked to build "telegram"
# after: names match exactly
channels:
telegram:
type: telegram
... Defensive patterns
Strategy: validation
Validate before calling
// before building channels, assert every requested name exists in config
for _, name := range wantedChannels {
if cfg.Channels[name] == nil {
return fmt.Errorf("channel %q missing from config.Channels — add a stanza or fix the name", name)
}
} Type guard
// guard the lookup itself
func hasChannelConfig(cfg *config.Config, name string) bool {
_, ok := cfg.Channels[name]
return ok
} Try / catch
// Go: if err from Build/Create contains "config not found", stop and fix config; retrying cannot help
Prevention
- Keep one source of truth for channel names (the channels map keys) and derive enabled-lists from it
- Names are case-sensitive and must match exactly
- Lint config at startup: every enabled channel must have a stanza
When it happens
Trigger: RegisterFactory/RegisterSafeFactory invoked for a channelName that is not a key in cfg.Channels (e.g. "telegram-bot" configured in an enabled-list but absent as a channels stanza); typo/mismatch between the enabled channel name and the YAML key; config loaded from a different file than expected so the stanza never existed.
Common situations: Renaming a channel in one place (enabled list, CLI flag) but not in the channels block; YAML indentation putting the channel stanza under the wrong parent; multiple config sources (file + env overrides) where the file providing the stanza fails to load; channel name case mismatch ("Slack" vs "slack").
Related errors
- channel %q: failed to decode settings: %w
- channel %q: expected %T settings, got %T
- slack_webhook: at least one webhook target is required
- teams_webhook: at least one webhook target is required
- must be in range 1-65535
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/223644659804f116.
Report an issue: GitHub.