{"record":{"id":"dca5a68aa3b77484","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-subscription-dca5a6","errorCode":"error-invalid-subscription","errorMessage":"error-invalid-subscription","messagePattern":"error-invalid-subscription","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/readMessages.ts","lineNumber":13,"sourceCode":"import type { IRoom, IUser } from '@rocket.chat/core-typings';\nimport { NotificationQueue, Subscriptions } from '@rocket.chat/models';\n\nimport { callbacks } from './callbacks';\nimport { notifyOnSubscriptionChangedByRoomIdAndUserId } from './notifyListener';\n\nexport async function readMessages(room: IRoom, uid: IUser['_id'], readThreads: boolean): Promise<void> {\n\tawait callbacks.run('beforeReadMessages', room._id, uid);\n\n\tconst projection = { ls: 1, tunread: 1, alert: 1, ts: 1 };\n\tconst sub = await Subscriptions.findOneByRoomIdAndUserId(room._id, uid, { projection });\n\tif (!sub) {\n\t\tthrow new Error('error-invalid-subscription');\n\t}\n\n\t// do not mark room as read if there are still unread threads\n\tconst alert = !!(sub.alert && !readThreads && sub.tunread && sub.tunread.length > 0);\n\n\tconst setAsReadResponse = await Subscriptions.setAsReadByRoomIdAndUserId(room._id, uid, readThreads, alert);\n\tif (setAsReadResponse.modifiedCount) {\n\t\tvoid notifyOnSubscriptionChangedByRoomIdAndUserId(room._id, uid);\n\t}\n\n\tawait NotificationQueue.clearQueueByUserId(uid);\n\n\tconst lastSeen = sub.ls || sub.ts;\n\tcallbacks.runAsync('afterReadMessages', room, { uid, lastSeen });\n}\n","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/readMessages.ts#L1-L29","documentation":"Plain Error('error-invalid-subscription') thrown by readMessages when Subscriptions.findOneByRoomIdAndUserId returns no document for the room+user pair. It means the caller tried to mark a room as read for a user who has no subscription in that room.","triggerScenarios":"Invoking the mark-read flow for a room the user never joined, or for a room whose subscription was already deleted (kick, leave, federation removal) while the client still had the room open.","commonSituations":"Stale browser tab of a room the user was kicked from; race between leaving a room and a pending mark-read request; automation calling subscriptions.read with stale membership data.","solutions":["Treat it as stale state: reload the user's subscription list and close the room view that produced the call.","Before calling, confirm a subscription exists for the (roomId, userId) pair.","In automation, verify the user is still a member before marking rooms read."],"exampleFix":"// before\nawait readMessages(room, uid, false); // throws error-invalid-subscription\n\n// after\nconst sub = await Subscriptions.findOneByRoomIdAndUserId(room._id, uid, { projection: { _id: 1 } });\nif (sub) {\n  await readMessages(room, uid, false);\n}","handlingStrategy":"validation","validationCode":"// server-side, before calling readMessages\nconst sub = await Subscriptions.findOneByRoomIdAndUserId(roomId, uid, { projection: { _id: 1 } });\nif (!sub) {\n  // user has no subscription here: refresh state and skip mark-read\n}","typeGuard":null,"tryCatchPattern":"try {\n  await readMessages(room, uid, readThreads);\n} catch (error: any) {\n  if (error?.message === 'error-invalid-subscription') {\n    // stale state, not fatal: reload subscriptions and drop the room view\n  }\n}","preventionTips":["Close or drop room views when the subscription is removed (kick/leave events).","Debounce mark-read calls when switching rooms quickly.","Check membership before calling subscriptions.read from automation."],"tags":["subscriptions","unread","rooms","stale-state"],"backgroundTag":"subscription-not-found","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}