{"record":{"id":"003e9631d39542c3","repo":"gotify/server","slug":"plugin-does-not-support-s","errorCode":null,"errorMessage":"plugin does not support %s","messagePattern":"plugin does not support (.+?)","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"api/plugin.go","lineNumber":415,"sourceCode":"\t\t}\n\t\tif err := instance.ValidateAndSetConfig(newConf); err != nil {\n\t\t\tctx.AbortWithError(400, err)\n\t\t\treturn\n\t\t}\n\t\tconf.Config = newconfBytes\n\t\tsuccessOrAbort(ctx, 500, c.DB.UpdatePluginConf(conf))\n\t})\n}\n\nfunc isPluginOwner(ctx *gin.Context, conf *model.PluginConf) bool {\n\treturn conf.UserID == auth.GetUserID(ctx)\n}\n\nfunc supportOrAbort(ctx *gin.Context, instance compat.PluginInstance, module compat.Capability) (aborted bool) {\n\tif compat.HasSupport(instance, module) {\n\t\treturn false\n\t}\n\tctx.AbortWithError(400, fmt.Errorf(\"plugin does not support %s\", module))\n\treturn true\n}\n","sourceCodeStart":397,"sourceCodeEnd":418,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/api/plugin.go#L397-L418","documentation":"supportOrAbort checks whether a plugin instance implements a required capability (compat.Capability) via compat.HasSupport. If the plugin lacks the module, the handler writes a 400 response with \"plugin does not support <capability>\" and aborts the request chain. It is a guard used by API handlers that delegate functionality to a pluggable backend.","triggerScenarios":"Calling any API handler that routes through supportOrAbort when the configured plugin instance does not implement the requested capability module (e.g. an auth or storage plugin lacking the module the endpoint requires).","commonSituations":"Deploying with a minimal or legacy plugin build that omits a capability; misconfiguring the plugin so a different instance (without the module) is selected; upgrading Gitea/woodpecker-style plugin interfaces and forgetting to enable a module.","solutions":["Use or configure a plugin instance that implements the required capability (verify with compat.HasSupport).","Check the plugin configuration so the endpoint resolves to the correct instance with the module enabled.","Upgrade or rebuild the plugin to a version that supports the missing module.","If the endpoint is not needed, stop calling it or guard the client code against 400 responses carrying this message."],"exampleFix":"// before: calling endpoint backed by a plugin without the module\nclient.Storage.Download(ctx, file) // -> 400 plugin does not support storage\n// after: pick an instance that supports the capability\nif !compat.HasSupport(instance, module) {\n    instance = loadInstanceWithModule(module)\n}","handlingStrategy":"validation","validationCode":"if !compat.HasSupport(instance, module) {\n    // skip the call or choose another instance\n    return\n}","typeGuard":"func supportsModule(instance compat.PluginInstance, module compat.Capability) bool {\n    return compat.HasSupport(instance, module)\n}","tryCatchPattern":null,"preventionTips":["Check capability support before invoking plugin-backed endpoints.","Pin plugin versions known to implement all required modules.","Validate plugin config at startup and fail fast on missing modules."],"tags":["plugin","capability","http-400"],"backgroundTag":"unsupported-plugin-capability","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"}