{"record":{"id":"46a84d23683fc513","repo":"RocketChat/Rocket.Chat","slug":"user-not-subscribed-to-room","errorCode":null,"errorMessage":"User not subscribed to room","messagePattern":"User not subscribed to room","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/meteor/app/apps/server/bridges/rooms.ts","lineNumber":330,"sourceCode":"\t\tconst users = await Users.findByIds(subs.map((user: { uid: string }) => user.uid)).toArray();\n\t\tconst userConverter = this.orch.getConverters().get('users');\n\t\treturn users.map((user: ICoreUser) => userConverter.convertToApp(user));\n\t}\n\n\tprotected async getUnreadByUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise<Array<IMessageRaw>> {\n\t\tthis.orch.debugLog(`The App ${appId} is getting the unread messages for the user: \"${uid}\" in the room: \"${roomId}\"`);\n\n\t\tconst messageConverter = this.orch.getConverters()?.get('messages');\n\t\tif (!messageConverter) {\n\t\t\tthrow new Error('Message converter not found');\n\t\t}\n\n\t\tconst subscription = await Subscriptions.findOneByRoomIdAndUserId(roomId, uid, { projection: { ls: 1 } });\n\n\t\tif (!subscription) {\n\t\t\tconst errorMessage = `No subscription found for user with ID \"${uid}\" in room with ID \"${roomId}\". This means the user is not subscribed to the room.`;\n\t\t\tthis.orch.debugLog(errorMessage);\n\t\t\tthrow new Error('User not subscribed to room');\n\t\t}\n\n\t\tconst lastSeen = subscription?.ls;\n\t\tif (!lastSeen) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst sort: Sort = options.sort?.createdAt ? { ts: options.sort.createdAt } : { ts: 1 };\n\n\t\tconst cursor = Messages.findVisibleByRoomIdBetweenTimestampsNotContainingTypes(\n\t\t\troomId,\n\t\t\tlastSeen,\n\t\t\tnew Date(),\n\t\t\t[],\n\t\t\t{\n\t\t\t\tlimit: options.limit,\n\t\t\t\tskip: options.skip,\n\t\t\t\tsort,","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/app/apps/server/bridges/rooms.ts#L312-L348","documentation":"Thrown by the rooms bridge getUnreadByUser when Subscriptions.findOneByRoomIdAndUserId finds no subscription for that user in that room. Rocket.Chat represents every membership as a subscription document, so absence means the user is not (or no longer) a member, and unread tracking for them is undefined.","triggerScenarios":"App calls getUnreadByUser(roomId, uid) for a user who never joined, left, or was kicked/removed from the room; roomId and uid swapped or malformed; calling it for a livechat visitor, which has no subscription document.","commonSituations":"Apps iterating all workspace users instead of room members; acting on a member list cached from before a kick; passing a username where a user id is expected.","solutions":["Fetch the actual members via the room reader (getMembers) and only query unread for those ids.","Verify both identifiers: roomId from a live room/message context, uid from a real user object.","Treat 'not subscribed' as an expected outcome: skip that user instead of failing the whole operation."],"exampleFix":"// before\nfor (const uid of allWorkspaceUserIds) {\n  const unread = await getUnreadByUser(roomId, uid); // throws for non-members\n}\n\n// after\nconst members = await this.read.getRoomReader().getMembers(roomId);\nfor (const member of members) {\n  const unread = await getUnreadByUser(roomId, member.id);\n}","handlingStrategy":"validation","validationCode":"const members = await this.read.getRoomReader().getMembers(roomId);\nconst isMember = members.some((m) => m.id === uid);\nif (!isMember) {\n  // skip unread query for this user\n}","typeGuard":"const isMemberOf = (members: Array<{ id: string }>, uid: string): boolean =>\n  members.some((m) => m.id === uid);","tryCatchPattern":"Catch and degrade: skip this user's unread computation and continue the loop; rethrow only if membership was verified immediately before.","preventionTips":["Never assume membership from cached lists; re-read members.","Source uid from user objects and roomId from room contexts, never free-text input."],"tags":["apps-engine","subscription","membership","room"],"backgroundTag":"user-not-in-room","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}