{"record":{"id":"5cfc4cffc2e08e60","repo":"gotify/server","slug":"application-does-not-exists","errorCode":null,"errorMessage":"application does not exists","messagePattern":"application does not exists","errorType":"http","errorClass":null,"httpStatus":404,"severity":"warning","filePath":"api/message.go","lineNumber":264,"sourceCode":"//\t        $ref: \"#/definitions/Error\"\n//\t  403:\n//\t    description: Forbidden\n//\t    schema:\n//\t        $ref: \"#/definitions/Error\"\n//\t  404:\n//\t    description: Not Found\n//\t    schema:\n//\t        $ref: \"#/definitions/Error\"\nfunc (a *MessageAPI) DeleteMessageWithApplication(ctx *gin.Context) {\n\twithID(ctx, \"id\", func(id uint) {\n\t\tapplication, err := a.DB.GetApplicationByID(id)\n\t\tif success := successOrAbort(ctx, 500, err); !success {\n\t\t\treturn\n\t\t}\n\t\tif application != nil && application.UserID == auth.GetUserID(ctx) {\n\t\t\tsuccessOrAbort(ctx, 500, a.DB.DeleteMessagesByApplication(id))\n\t\t} else {\n\t\t\tctx.AbortWithError(404, errors.New(\"application does not exists\"))\n\t\t}\n\t})\n}\n\n// DeleteMessage deletes a message with an id.\n// swagger:operation DELETE /message/{id} message deleteMessage\n//\n// Deletes a message with an id.\n//\n//\t---\n//\tproduces: [application/json]\n//\tsecurity: [clientTokenAuthorizationHeader: [], clientTokenHeader: [], clientTokenQuery: [], basicAuth: []]\n//\tparameters:\n//\t- name: id\n//\t  in: path\n//\t  description: the message id\n//\t  required: true\n//\t  type: integer","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/api/message.go#L246-L282","documentation":"An HTTP 404 raised by the DeleteMessages endpoint when the application for the given path ID is nil or is owned by a different user. Only the owner may delete an application's messages; the library returns 404 (not 403) in both the missing and not-owned cases. The message string contains a typo ('exists' vs 'exist') compared to the sibling error at message.go:191, but the semantics are identical.","triggerScenarios":"DELETE /application/{id}/message (delete all messages of an application) where the ID (1) does not exist, (2) refers to an already-deleted application, or (3) belongs to another user. Double-deletion is a classic trigger: the first DELETE succeeds, the second hits this 404.","commonSituations":"Retry logic re-running a delete that already succeeded; cleanup scripts iterating IDs captured before deletion; deleting messages of another user's application; wrong instance/environment in CI scripts.","solutions":["Confirm the application ID exists and is yours via GET /application before deleting its messages.","Treat a 404 here after a successful delete as success (idempotency): messages are already gone.","If the whole application was removed, skip the message-delete step entirely.","Check the script is authenticated as the owning user, not another account."],"exampleFix":"// before\nawait fetch(`/application/${appId}/message`, { method: 'DELETE' });\nawait fetch(`/application/${appId}/message`, { method: 'DELETE' }); // second call -> 404 'application does not exists'\n\n// after\nconst res = await fetch(`/application/${appId}/message`, { method: 'DELETE' });\nif (res.status === 404) console.log('already deleted or not owned — treat as success');","handlingStrategy":"validation","validationCode":"// Confirm ownership before issuing the bulk delete\nconst apps = await fetch('/application', { headers: { 'X-Gotify-Key': token } }).then(r => r.json());\nif (!apps.some(a => a.id === appId)) {\n  console.log(`skip: application ${appId} already gone or not owned`);\n  return; // treat as already-deleted\n}","typeGuard":null,"tryCatchPattern":"// Make bulk delete idempotent\nconst res = await fetch(`/application/${appId}/message`, { method: 'DELETE' });\nif (res.status === 404) return; // already deleted or not owned — goal state reached\nif (!res.ok) throw new Error(`bulk delete failed: ${res.status}`);","preventionTips":["Treat 404 from this endpoint as success in cleanup/CI scripts to survive double-deletes.","Delete message state before deleting the application itself in teardown scripts.","Capture application IDs fresh from GET /application, not from stale config.","404 covers 'not owned' too — verify you authenticate as the owner before assuming deletion failed."],"tags":["http-404","gotify","rest-api","delete-idempotency","ownership-check"],"backgroundTag":"http-404-resource-not-found","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"}