{"record":{"id":"987c289a63031724","repo":"louislam/uptime-kuma","slug":"notification-not-found","errorCode":null,"errorMessage":"notification not found","messagePattern":"notification not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/notification.js","lineNumber":272,"sourceCode":"            throw new Error(\"Notification type is not supported\");\n        }\n    }\n\n    /**\n     * Save a notification\n     * @param {object} notification Notification to save\n     * @param {?number} notificationID ID of notification to update\n     * @param {number} userID ID of user who adds notification\n     * @returns {Promise<Bean>} Notification that was saved\n     */\n    static async save(notification, notificationID, userID) {\n        let bean;\n\n        if (notificationID) {\n            bean = await R.findOne(\"notification\", \" id = ? AND user_id = ? \", [notificationID, userID]);\n\n            if (!bean) {\n                throw new Error(\"notification not found\");\n            }\n        } else {\n            bean = R.dispense(\"notification\");\n        }\n\n        // applyExisting is one time only, don't save it to database.\n        const applyExisting = notification.applyExisting || false;\n        notification.applyExisting = false;\n\n        bean.name = notification.name;\n        bean.user_id = userID;\n        bean.config = JSON.stringify(notification);\n        bean.is_default = notification.isDefault || false;\n        await R.store(bean);\n\n        if (applyExisting) {\n            await applyNotificationEveryMonitor(bean.id, userID);\n        }","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/notification.js#L254-L290","documentation":"Thrown by Notification.save during an update (notificationID provided) when no notification row matches both the given id AND the user_id. The AND user_id clause enforces ownership: a missing row means either the id does not exist or it belongs to a different user.","triggerScenarios":"Editing a notification whose id was already deleted, a stale id held in the browser after another session deleted it, or a user_id mismatch (wrong user editing another's notification, possibly via a tampered request).","commonSituations":"Two browser tabs open: one deletes the notification while the other tries to save; client-side id caching after deletion; or privilege escalation attempt via crafted notificationID.","solutions":["Refresh the notifications list in the UI to discard stale ids and retry the edit.","Confirm the authenticated user actually owns the notification (check user_id in the DB row).","If the notification was deleted intentionally, create a new one instead of updating the old id.","Inspect server logs to confirm the (notificationID, userID) pair being passed."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Verify ownership before attempting an update\nasync function notificationExistsForUser(notificationID, userID) {\n    const bean = await R.findOne(\"notification\", \" id = ? AND user_id = ? \", [notificationID, userID]);\n    return bean != null;\n}\nif (notificationID && !(await notificationExistsForUser(notificationID, userID))) {\n    throw new Error(\"notification not found\");\n}","typeGuard":"/** True when a bean was found for the (id, user) pair. */\nfunction isOwnedNotification(bean) { return bean != null; }","tryCatchPattern":"try {\n    const bean = await R.findOne(\"notification\", \" id = ? AND user_id = ? \", [notificationID, userID]);\n    if (!bean) throw new Error(\"notification not found\");\n    // ...proceed with update\n} catch (err) { throw err; }","preventionTips":["Refresh the notifications list before editing to drop stale ids.","Confirm the logged-in user owns the target notification.","Avoid concurrent edits across tabs."],"tags":["notification","ownership","not-found","crud"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}