crowdsecurity/crowdsec · error
loading config
Error message
loading config: %w
What it means
PluginBroker.Init sets up the notification plugin system and first loads plugin configurations from the notifications yaml directory (loadConfig). Any error reading/parsing that directory (missing dir, unreadable files, invalid yaml configs) is wrapped as 'loading config'. Without it, no notification plugins can be dispatched and LAPI startup aborts.
Solutions
- Check the directory exists and is readable: `ls -la /etc/crowdsec/notifications/`; create it if missing.
- Run `yamllint /etc/crowdsec/notifications/*.yaml` and fix the syntax/schema error in the offending file (the wrapped error names it).
- Validate the plugin config structure against the stock example for that plugin type (email.yaml, http.yaml, slack.yaml).
- Fix file permissions so the crowdsec user can read the notification configs: `chown -R crowdsec:crowdsec /etc/crowdsec/notifications`.
- Temporarily move suspect yaml files out of the directory and restart to isolate the broken one.
Example fix
// before (notifications/email.yaml, missing required 'format') type: email name: email_default // after type: email name: email_default format: >- Crowdsec alert ...
Defensive patterns
Strategy: validation
Validate before calling
dir := configPaths.NotificationDir
info, err := os.Stat(dir)
if err != nil {
return fmt.Errorf("notification dir %s missing: %w", dir, err)
}
if !info.IsDir() { return fmt.Errorf("%s is not a directory", dir) }
files, _ := os.ReadDir(dir)
for _, f := range files {
if strings.HasSuffix(f.Name(), ".yaml") || strings.HasSuffix(f.Name(), ".yml") {
if _, err := os.ReadFile(filepath.Join(dir, f.Name())); err != nil {
return fmt.Errorf("unreadable notification config %s: %w", f.Name(), err)
}
}
} Try / catch
if err := broker.Init(ctx, configPaths, pluginCfg, profileConfigs); err != nil {
if strings.Contains(err.Error(), "loading config") {
// fix or remove the broken yaml in notification_dir, then retry
}
return err
} Prevention
- Ensure /etc/crowdsec/notifications exists with crowdsec-readable permissions after installs.
- Validate every notification yaml (yamllint + schema check against the plugin's example).
- Re-validate notification configs after crowdsec upgrades as plugin config schemas evolve.
- Keep plugin configs owned by crowdsec:crowdsec with mode 640/644.
When it happens
Trigger: config_paths.notification_dir (default /etc/crowdsec/notifications/) is missing, unreadable, or contains yaml files whose plugin configs fail to parse (wrong structure for http/email/slack/splunk plugins).
Common situations: Fresh installs where the notifications dir wasn't created; hand-written notification yamls with malformed fields; packages upgraded such that old plugin config keys no longer match; permission issues after manual edits with root-only files.
Understand the failure class
Background: "Config file not found": what it means and how to fix it in docker-sync, Maven, Vagrant, Turborepo and other tools — this error's family across 60 libraries.
Related errors
- plugin : config not found
- binary for plugin not found
- config file for plugin
- loading plugin
- plugin broker
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/52f788a748d4b4f3.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/csplugin/broker.go:104
*pc = PluginConfig(aux)
return nil
}
type PluginConfigList []PluginConfig
func (pb *PluginBroker) Init(ctx context.Context, pluginCfg *csconfig.PluginCfg, profileConfigs []*csconfig.ProfileCfg, configPaths *csconfig.ConfigurationPaths) error {
pb.PluginChannel = make(chan models.ProfileAlert)
pb.notificationPluginByName = make(map[string]protobufs.NotifierServer)
pb.pluginMap = make(map[string]plugin.Plugin)
pb.pluginConfigByName = make(map[string]PluginConfig)
pb.alertsByPluginName = make(map[string][]*models.Alert)
pb.profileConfigs = profileConfigs
pb.pluginProcConfig = pluginCfg
pb.pluginsTypesToDispatch = make(map[string]struct{})
if err := pb.loadConfig(configPaths.NotificationDir); err != nil {
return fmt.Errorf("loading config: %w", err)
}
if err := pb.loadPlugins(ctx, configPaths.PluginDir); err != nil {
return fmt.Errorf("loading plugin: %w", err)
}
pb.watcher = PluginWatcher{}
pb.watcher.Init(pb.pluginConfigByName, pb.alertsByPluginName)
return nil
}
func (pb *PluginBroker) ensureBackoff() backoffFactory {
if pb.newBackoff == nil {
pb.newBackoff = defaultBackoffFactory
}
return pb.newBackoff
}View on GitHub (pinned to 909b515798)