{"record":{"id":"223644659804f116","repo":"sipeed/picoclaw","slug":"channel-q-config-not-found","errorCode":null,"errorMessage":"channel %q: config not found","messagePattern":"channel %q: config not found","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/registry.go","lineNumber":47,"sourceCode":"// RegisterSafeFactory is a convenience wrapper that handles GetDecoded() error checking\n// and type assertion, reducing boilerplate in channel init() functions.\n//\n// Usage:\n//\n//\tfunc init() {\n//\t    channels.RegisterSafeFactory(config.ChannelTelegram,\n//\t        func(bc *config.Channel, c *config.TelegramSettings, b *bus.MessageBus) (channels.Channel, error) {\n//\t            return NewTelegramChannel(bc, c, b)\n//\t        })\n//\t}\nfunc RegisterSafeFactory[S any](\n\tchannelType string,\n\tctor func(bc *config.Channel, settings *S, bus *bus.MessageBus) (Channel, error),\n) {\n\tRegisterFactory(channelType, func(channelName, _ string, cfg *config.Config, b *bus.MessageBus) (Channel, error) {\n\t\tbc := cfg.Channels[channelName]\n\t\tif bc == nil {\n\t\t\treturn nil, fmt.Errorf(\"channel %q: config not found\", channelName)\n\t\t}\n\t\tdecoded, err := bc.GetDecoded()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"channel %q: failed to decode settings: %w\", channelName, err)\n\t\t}\n\t\tsettings, ok := decoded.(*S)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"channel %q: expected %T settings, got %T\", channelName, (*S)(nil), decoded)\n\t\t}\n\t\treturn ctor(bc, settings, b)\n\t})\n}\n\n// getFactory looks up a channel factory by name.\nfunc getFactory(name string) (ChannelFactory, bool) {\n\tfactoriesMu.RLock()\n\tdefer factoriesMu.RUnlock()\n\tf, ok := factories[name]","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/registry.go#L29-L65","documentation":"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.","triggerScenarios":"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.","commonSituations":"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\").","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:."],"exampleFix":"# before: enabled name has no matching stanza\nchannels:\n  telegram-main:\n    type: telegram\n    ...\n# manager asked to build \"telegram\"\n\n# after: names match exactly\nchannels:\n  telegram:\n    type: telegram\n    ...","handlingStrategy":"validation","validationCode":"// before building channels, assert every requested name exists in config\nfor _, name := range wantedChannels {\n    if cfg.Channels[name] == nil {\n        return fmt.Errorf(\"channel %q missing from config.Channels — add a stanza or fix the name\", name)\n    }\n}","typeGuard":"// guard the lookup itself\nfunc hasChannelConfig(cfg *config.Config, name string) bool {\n    _, ok := cfg.Channels[name]\n    return ok\n}","tryCatchPattern":"// Go: if err from Build/Create contains \"config not found\", stop and fix config; retrying cannot help","preventionTips":["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"],"tags":["registry","config","channel-setup","startup"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}