gotify/server · error
plugin with module path %s is present at least twice
Error message
plugin with module path %s is present at least twice
What it means
LoadPlugin registers a compat plugin keyed by its PluginInfo().ModulePath. Since m.plugins is a map, duplicate module paths would silently overwrite, so loading a second plugin with an already-registered module path returns this error instead.
Source
Thrown at plugin/manager.go:252
if err != nil {
return pluginFileLoadError{name, err}
}
compatPlugin, err := compat.Wrap(pRaw)
if err != nil {
return pluginFileLoadError{name, err}
}
if err := m.LoadPlugin(compatPlugin); err != nil {
return pluginFileLoadError{name, err}
}
}
return nil
}
// LoadPlugin loads a compat plugin, exported to sideload plugins for testing purposes.
func (m *Manager) LoadPlugin(compatPlugin compat.Plugin) error {
modulePath := compatPlugin.PluginInfo().ModulePath
if _, ok := m.plugins[modulePath]; ok {
return fmt.Errorf("plugin with module path %s is present at least twice", modulePath)
}
m.plugins[modulePath] = compatPlugin
return nil
}
// InitializeForUserID initializes all plugin instances for a given user.
func (m *Manager) InitializeForUserID(userID uint) error {
m.mutex.Lock()
defer m.mutex.Unlock()
user, err := m.db.GetUserByID(userID)
if err != nil {
return err
}
if user != nil {
return m.initializeForUser(*user)
}
return fmt.Errorf("user with id %d not found", userID)View on GitHub (pinned to 14bfc25627)
Solutions
- Remove the duplicate .so file from the plugin directory
- Ensure each plugin's ModulePath in its PluginInfo is unique
- Keep only one version of each plugin on disk
Defensive patterns
Strategy: validation
Validate before calling
paths := map[string]bool{}
for _, f := range pluginFiles {
compat, err := load(f)
if err != nil { continue }
mp := compat.PluginInfo().ModulePath
if paths[mp] {
log.Printf("duplicate module path %s in %s", mp, f)
continue
}
paths[mp] = true
} Try / catch
if err := mgr.LoadPlugin(cp); err != nil {
if strings.Contains(err.Error(), "present at least twice") {
log.Printf("skipping duplicate plugin: %v", err)
return nil
}
return err
} Prevention
- Keep only one .so per module in the plugin directory
- Give each plugin a unique ModulePath
- Clean old plugin versions before deploying new ones
When it happens
Trigger: Calling LoadPlugin (directly or via loadPlugins) with two plugin files that declare the same ModulePath — e.g. the same plugin .so copied under two filenames in the plugin directory.
Common situations: Docker images containing both foo.so and foo-v2.so built from the same module, or sideloading a test plugin that duplicates a production plugin.
Related errors
- user does not exist and auto-registration is disabled
- username already exists
- issuer url %q is not a valid url: %w
- username claim %q is missing
- a local user with the username %s already exists and linking
AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05).
Data as JSON: /api/errors/4193dfc77fbb6026.
Report an issue: GitHub.