m1k1o/neko · error
plugin's '%s' dependency: %w
Error message
plugin's '%s' dependency: %w
What it means
The plugin manager starts plugins as a dependency graph: startPlugin first recursively starts everything in a.dependsOn before starting the plugin itself. If a dependency's own startPlugin fails, the error is wrapped as fmt.Errorf("plugin's '%s' dependency: %w", a.plugin.Name(), err), attributing the failure to the dependent plugin's dependency chain while preserving the underlying cause via %w.
Source
Thrown at server/internal/plugins/dependency.go:46
plug, ok := dep.findPlugin(name)
if ok {
return plug, true
}
}
return nil, false
}
func (a *dependency) startPlugin(pm types.PluginManagers) error {
if a.invoked {
return nil
}
a.invoked = true
for _, do := range a.dependsOn {
if err := do.startPlugin(pm); err != nil {
return fmt.Errorf("plugin's '%s' dependency: %w", a.plugin.Name(), err)
}
}
err := a.plugin.Start(pm)
if err != nil {
return fmt.Errorf("plugin '%s' failed to start: %w", a.plugin.Name(), err)
}
a.logger.Info().Str("plugin", a.plugin.Name()).Msg("plugin started")
return nil
}
type dependiencies struct {
deps map[string]*dependency
logger zerolog.Logger
}
func (d *dependiencies) addPlugin(plugin types.Plugin) error {View on GitHub (pinned to b0f01cedea)
Solutions
- Read the wrapped chain to the innermost error — it names the actual plugin whose Start() failed
- Enable/start the required dependency plugin first, or add it to the configuration
- Fix the root cause in the dependency plugin (config, external service, resource availability)
- If the dependency is optional, remove it from the plugin's dependsOn list
Example fix
// before (config)
plugins: {"whiteboard": {"enabled": true}, "chat": {"enabled": false}}
// after (chat is a whiteboard dependency)
plugins: {"whiteboard": {"enabled": true}, "chat": {"enabled": true}} Defensive patterns
Strategy: try-catch
Validate before calling
for _, dep := range plugin.DependsOn() {
if !pm.IsRegistered(dep) {
return fmt.Errorf("plugin %s requires dependency %s which is not enabled", plugin.Name(), dep)
}
} Type guard
func dependenciesEnabled(deps []string, enabled map[string]bool) bool {
for _, d := range deps {
if !enabled[d] {
return false
}
}
return true
} Try / catch
if err := manager.Start("whiteboard"); err != nil {
var perr *pluginError
if errors.As(err, &perr) && strings.Contains(err.Error(), "dependency") {
log.Printf("dependency chain failed, disabling plugin: %v", err)
} else {
return err
}
} Prevention
- Before enabling a plugin in config, verify every plugin in its dependsOn list is also enabled
- Use errors.As/errors.Is to unwrap the %w chain to the root cause instead of string matching
- Keep dependency lists updated when renaming or removing plugins
When it happens
Trigger: Calling Start on a plugin whose dependsOn list contains a plugin that fails to start — either its own Start() returned an error or a deeper transitive dependency failed, so each level of the chain wraps the error with its own "plugin's 'X' dependency" prefix.
Common situations: A plugin enabled in config requires another plugin that is disabled or missing; the dependency plugin panics or returns an error during Start (bad config, failed port bind, missing external service); circular or stale dependency lists after a plugin rename or upgrade.
Related errors
- plugin '%s' failed to start: %w
- plugin '%s' already added
- cyclical dependency detected: '%s' <-> '%s'
- unable to unmarshal %s plugin settings from global settings:
- unable to unmarshal %s plugin settings from profile: %w
AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01).
Data as JSON: /api/errors/195f1932d177f7c8.
Report an issue: GitHub.