{"record":{"id":"0d7fa72b7c513e4e","repo":"gotify/server","slug":"newgotifyplugininstance-signature-mismatch-func-c","errorCode":null,"errorMessage":"NewGotifyPluginInstance signature mismatch, func(ctx plugin.UserContext) plugin.Plugin expected, got %T","messagePattern":"NewGotifyPluginInstance signature mismatch, func\\(ctx plugin\\.UserContext\\) plugin\\.Plugin expected, got %T","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugin/compat/wrap.go","lineNumber":28,"sourceCode":"\n// Wrap wraps around a raw go plugin to provide typesafe access.\nfunc Wrap(p *plugin.Plugin) (Plugin, error) {\n\tgetInfoHandle, err := p.Lookup(\"GetGotifyPluginInfo\")\n\tif err != nil {\n\t\treturn nil, errors.New(\"missing GetGotifyPluginInfo symbol\")\n\t}\n\tswitch getInfoHandle := getInfoHandle.(type) {\n\tcase func() papiv1.Info:\n\t\tv1 := PluginV1{}\n\n\t\tv1.Info = getInfoHandle()\n\t\tnewInstanceHandle, err := p.Lookup(\"NewGotifyPluginInstance\")\n\t\tif err != nil {\n\t\t\treturn nil, errors.New(\"missing NewGotifyPluginInstance symbol\")\n\t\t}\n\t\tconstructor, ok := newInstanceHandle.(func(ctx papiv1.UserContext) papiv1.Plugin)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"NewGotifyPluginInstance signature mismatch, func(ctx plugin.UserContext) plugin.Plugin expected, got %T\", newInstanceHandle)\n\t\t}\n\t\tv1.Constructor = constructor\n\t\treturn v1, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown plugin version (unrecogninzed GetGotifyPluginInfo signature %T)\", getInfoHandle)\n\t}\n}\n","sourceCodeStart":10,"sourceCodeEnd":36,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/plugin/compat/wrap.go#L10-L36","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nfunc NewGotifyPluginInstance(ctx plugin.UserContext, cfg Config) *MyPlugin { ... }\n// after\nfunc NewGotifyPluginInstance(ctx plugin.UserContext) plugin.Plugin { return &MyPlugin{} }","handlingStrategy":"type-guard","validationCode":"sym, err := p.Lookup(\"NewGotifyPluginInstance\")\nif err != nil { panic(\"missing symbol\") }\n_, ok := sym.(func(papiv1.UserContext) papiv1.Plugin)\nif !ok {\n    fmt.Printf(\"wrong signature: %T\\n\", sym)\n}","typeGuard":"func isValidConstructor(h interface{}) bool {\n    _, ok := h.(func(papiv1.UserContext) papiv1.Plugin)\n    return ok\n}","tryCatchPattern":"compat, err := wrapper.Wrap(pluginPath)\nif err != nil {\n    if strings.Contains(err.Error(), \"signature mismatch\") {\n        log.Printf(\"plugin %s built against wrong plugin-api: %v\", pluginPath, err)\n        return nil // skip plugin\n    }\n    return err\n}","preventionTips":["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"],"tags":["plugin","type-assertion","compatibility"],"backgroundTag":"plugin-constructor-signature-mismatch","analyzedSha":"14bfc256276775c425f988d621dccfe705de18ac","analyzedAt":"2026-09-05T12:52:36.781Z","contentChangedAt":"2026-09-05T12:52:36.781Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}