{"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":"Error","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/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/app/apps/server/bridges/rooms.ts#L312-L348","documentation":"Thrown by getUnreadByUser when Subscriptions.findOneByRoomIdAndUserId returns no document for the given room/user pair. In Rocket.Chat a user must have an active subscription (membership) to a room before message-read state is tracked, so the bridge refuses to compute unread messages for a non-member.","triggerScenarios":"An app calls getUnreadByUser(roomId, uid) for a user who has never joined the room, was removed from it, or whose subscription was deleted. The debug log records the exact room and user ids.","commonSituations":"App iterates a list of user ids that includes non-members; user left or was kicked between the app gathering the list and reading unread counts; livechat visitors or bot users queried against a room they are not subscribed to.","solutions":["Filter the user list to actual room members before calling getUnreadByUser (use the room's membership readers).","Wrap the call per-user in try/catch and skip users who are not subscribed.","Re-fetch membership right before the call if the room's roster can change concurrently."],"exampleFix":"// before\nfor (const uid of allUserIds) {\n  const unread = await read.getRoomReader().getUnreadByUser(roomId, uid);\n}\n\n// after\nfor (const uid of allUserIds) {\n  try {\n    const unread = await read.getRoomReader().getUnreadByUser(roomId, uid);\n  } catch (err) {\n    // user is not a member of the room; skip\n    continue;\n  }\n}","handlingStrategy":"validation","validationCode":"const members = await read.getRoomReader().getMembers(roomId);\nconst memberIds = new Set(members.map((m) => m.id));\nconst safeUserIds = allUserIds.filter((uid) => memberIds.has(uid));","typeGuard":null,"tryCatchPattern":"for (const uid of allUserIds) {\n  try {\n    const unread = await read.getRoomReader().getUnreadByUser(roomId, uid);\n  } catch (err) {\n    if (err instanceof Error && err.message === 'User not subscribed to room') {\n      continue;\n    }\n    throw err;\n  }\n}","preventionTips":["Derive the user list from the room's actual subscriptions.","Skip non-members rather than crashing a batch operation.","Re-check membership if the roster can change between gather and use."],"tags":["apps-engine","rooms","subscriptions","membership","validation"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}