gotify/server · error
NewGotifyPluginInstance signature mismatch, func(ctx plugin.
Error message
NewGotifyPluginInstance signature mismatch, func(ctx plugin.UserContext) plugin.Plugin expected, got %T
What it means
Wrap loads a Gotify plugin binary and checks that its exported NewGotifyPluginInstance symbol has the exact type func(plugin.UserContext) plugin.Plugin. If the type assertion fails because the plugin's constructor has a different signature (wrong API version, extra params, different return type), this error is returned.
Source
Thrown at plugin/compat/wrap.go:28
// Wrap wraps around a raw go plugin to provide typesafe access.
func Wrap(p *plugin.Plugin) (Plugin, error) {
getInfoHandle, err := p.Lookup("GetGotifyPluginInfo")
if err != nil {
return nil, errors.New("missing GetGotifyPluginInfo symbol")
}
switch getInfoHandle := getInfoHandle.(type) {
case func() papiv1.Info:
v1 := PluginV1{}
v1.Info = getInfoHandle()
newInstanceHandle, err := p.Lookup("NewGotifyPluginInstance")
if err != nil {
return nil, errors.New("missing NewGotifyPluginInstance symbol")
}
constructor, ok := newInstanceHandle.(func(ctx papiv1.UserContext) papiv1.Plugin)
if !ok {
return nil, fmt.Errorf("NewGotifyPluginInstance signature mismatch, func(ctx plugin.UserContext) plugin.Plugin expected, got %T", newInstanceHandle)
}
v1.Constructor = constructor
return v1, nil
default:
return nil, fmt.Errorf("unknown plugin version (unrecogninzed GetGotifyPluginInfo signature %T)", getInfoHandle)
}
}
View on GitHub (pinned to 14bfc25627)
Solutions
- Rebuild the plugin against the current gotify-android/plugin-api version
- Change the plugin's exported function to: func NewGotifyPluginInstance(ctx plugin.UserContext) plugin.Plugin
- Ensure the constructor returns the plugin.Plugin interface, not a concrete struct pointer
- Check GetGotifyPluginInfo's version marker matches the version branch Wrap expects
Example fix
// before
func NewGotifyPluginInstance(ctx plugin.UserContext, cfg Config) *MyPlugin { ... }
// after
func NewGotifyPluginInstance(ctx plugin.UserContext) plugin.Plugin { return &MyPlugin{} } Defensive patterns
Strategy: type-guard
Validate before calling
sym, err := p.Lookup("NewGotifyPluginInstance")
if err != nil { panic("missing symbol") }
_, ok := sym.(func(papiv1.UserContext) papiv1.Plugin)
if !ok {
fmt.Printf("wrong signature: %T\n", sym)
} Type guard
func isValidConstructor(h interface{}) bool {
_, ok := h.(func(papiv1.UserContext) papiv1.Plugin)
return ok
} Try / catch
compat, err := wrapper.Wrap(pluginPath)
if err != nil {
if strings.Contains(err.Error(), "signature mismatch") {
log.Printf("plugin %s built against wrong plugin-api: %v", pluginPath, err)
return nil // skip plugin
}
return err
} Prevention
- Pin the plugin-api dependency version in the plugin's go.mod
- Rebuild all plugins when upgrading gotify
- Add a unit test type-asserting the constructor
- Keep the constructor signature minimal: ctx in, plugin.Plugin out
When it happens
Trigger: loadPlugins or tests call Wrap on a compiled plugin whose NewGotifyPluginInstance does not type-assert to func(ctx papiv1.UserContext) papiv1.Plugin, e.g. the plugin was built against a different plugin-api version or returns a concrete type instead of plugin.Plugin.
Common situations: Plugin authors upgrade gotify but rebuild plugins against an old plugin-api, or hand-write the constructor with a wrong return type or extra parameters.
Related errors
- unknown plugin version (unrecogninzed GetGotifyPluginInfo si
- plugin does not support %s
- error while reading directory %s
- plugin with module path %s is present at least twice
- user with id %d not found
AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05).
Data as JSON: /api/errors/0d7fa72b7c513e4e.
Report an issue: GitHub.