m1k1o/neko · error
plugin '%s' already added
Error message
plugin '%s' already added
What it means
Returned by plugins.Manager's dependency graph (addPlugin) when a plugin with the same name is registered twice and the existing dependency entry already holds a plugin instance. The registry keys plugins by plugin.Name(), so names must be unique; a duplicate would silently overwrite the first registration, so it is rejected.
Source
Thrown at server/internal/plugins/dependency.go:71
}
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 {
pluginName := plugin.Name()
plug, ok := d.deps[pluginName]
if !ok {
plug = &dependency{}
} else if plug.plugin != nil {
return fmt.Errorf("plugin '%s' already added", pluginName)
}
plug.plugin = plugin
plug.logger = d.logger
d.deps[pluginName] = plug
dplug, ok := plugin.(types.DependablePlugin)
if !ok {
return nil
}
for _, depName := range dplug.DependsOn() {
dependsOn, ok := d.deps[depName]
if !ok {
dependsOn = &dependency{}
} else if dependsOn.plugin != nil {
// if there is a cyclical dependency, break it and return error
if tdep, ok := dependsOn.findPlugin(pluginName); ok {View on GitHub (pinned to b0f01cedea)
Solutions
- Give each plugin a unique value from Name() (e.g. include a namespace or version)
- Check the load/init flow for duplicate registration of the same plugin and load it only once
- If reloading, remove the old dependency entry (delete from d.deps) before calling addPlugin again
- Wrap addPlugin in an errors.Is check for this message and log which plugin name collided
Example fix
// before
func (p *myPlugin) Name() string { return "filetransfer" }
// two copies loaded -> "plugin 'filetransfer' already added"
// after
func (p *myPlugin) Name() string {
if p.variant == "beta" { return "filetransfer-beta" }
return "filetransfer"
} Defensive patterns
Strategy: validation
Validate before calling
if _, ok := manager.plugins.findPlugin(p.Name()); ok {
return fmt.Errorf("plugin %q already registered", p.Name())
}
return manager.plugins.addPlugin(p) Try / catch
if err := manager.plugins.addPlugin(p); err != nil {
if strings.Contains(err.Error(), "already added") {
log.Warn().Str("plugin", p.Name()).Msg("plugin already registered, skipping")
return nil
}
return err
} Prevention
- Enforce unique plugin.Name() values via a unit test over all registered plugins
- Load each plugin artifact exactly once at startup
- For hot-reload, explicitly remove the old dependency entry before re-adding
When it happens
Trigger: Calling manager.plugins.addPlugin(p) (directly or via Manager.load) twice with two plugin objects whose Name() returns the same string, while the earlier plugin was never removed (no cyclical-break/cleanup deleted its entry).
Common situations: Registering the same plugin binary/so file twice in config; two builds of one plugin both reporting the same Name(); a plugin reload path that forgot to delete the old entry from d.deps; embedding a plugin in two bundle plugins that are both loaded.
Related errors
- cyclical dependency detected: '%s' <-> '%s'
- unable to unmarshal %s plugin settings from global settings:
- unable to unmarshal %s plugin settings from profile: %w
- not a valid plugin
- failed to add plugin: %w
AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01).
Data as JSON: /api/errors/0567881eb1314c41.
Report an issue: GitHub.