gotify/server · warning
unknown plugin
Error message
unknown plugin
What it means
The plugin update/delete handler returns 404 'unknown plugin' when no plugin config exists for the given ID or the authenticated user is not the plugin's owner (isPluginOwner fails). Like message deletion, ownership mismatches are masked as not-found.
Source
Thrown at api/plugin.go:132
// description: Forbidden
// schema:
// $ref: "#/definitions/Error"
// 404:
// description: Not Found
// schema:
// $ref: "#/definitions/Error"
// 500:
// description: Internal Server Error
// schema:
// $ref: "#/definitions/Error"
func (c *PluginAPI) EnablePlugin(ctx *gin.Context) {
withID(ctx, "id", func(id uint) {
conf, err := c.DB.GetPluginConfByID(id)
if success := successOrAbort(ctx, 500, err); !success {
return
}
if conf == nil || !isPluginOwner(ctx, conf) {
ctx.AbortWithError(404, errors.New("unknown plugin"))
return
}
_, err = c.Manager.Instance(id)
if err != nil {
ctx.AbortWithError(404, errors.New("plugin instance not found"))
return
}
if err := c.Manager.SetPluginEnabled(id, true); err == plugin.ErrAlreadyEnabledOrDisabled {
ctx.AbortWithError(400, err)
} else if err != nil {
ctx.AbortWithError(500, err)
}
})
}
// DisablePlugin disables a plugin.
// swagger:operation POST /plugin/{id}/disable plugin disablePlugin
//View on GitHub (pinned to 14bfc25627)
Solutions
- List your plugins (GET /plugin) to get the correct ID.
- Authenticate as the owner of the plugin configuration.
- Reinstall/upload the plugin if it was removed, then retry with the new ID.
- Refresh the admin UI to clear stale plugin IDs.
Example fix
// before DELETE /plugin/12 // owned by another user // after GET /plugin // find own plugin id, then DELETE /plugin/<own-id>
Defensive patterns
Strategy: validation
Validate before calling
const plugins = await fetch('/plugin', {headers:{'X-Gotify-Key': token}}).then(r=>r.json());
if (!plugins.some(p => p.id === id)) throw new Error('plugin ' + id + ' not found/not owned; abort'); Type guard
function ownsPlugin(plugins, id) { return plugins.some(p => p.id === id); } Try / catch
try {
await gotify.delete(`/plugin/${id}`);
} catch (e) {
if (e.response?.status === 404) {
const list = await gotify.get('/plugin');
id = list[0]?.id; // recover with a valid owned id, or surface to user
} else throw e;
} Prevention
- Discover plugin IDs via GET /plugin instead of hardcoding.
- Refresh UI state after plugin install/uninstall.
- Perform plugin admin actions as the owning user.
- Treat 404 as 'gone or not owned'.
When it happens
Trigger: DELETE/PUT /plugin/:id with an ID that was never registered, was removed, or belongs to another user's plugin conf.
Common situations: Operating on a plugin ID from another account; referencing a plugin ID after Gotify restarted and plugin confs changed; stale UI state after the plugin was uninstalled; typo in the id path parameter.
Related errors
- client not found
- application does not exist
- application does not exists
- message does not exist
- client not found
AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05).
Data as JSON: /api/errors/dc4aaa7497b29870.
Report an issue: GitHub.