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

  1. Remove the duplicate .so file from the plugin directory
  2. Ensure each plugin's ModulePath in its PluginInfo is unique
  3. 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

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


AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05). Data as JSON: /api/errors/4193dfc77fbb6026. Report an issue: GitHub.