RocketChat/Rocket.Chat · error

User not subscribed to room

Error message

User not subscribed to room

What it means

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.

Solutions

  1. Fetch the actual members via the room reader (getMembers) and only query unread for those ids.
  2. Verify both identifiers: roomId from a live room/message context, uid from a real user object.
  3. Treat 'not subscribed' as an expected outcome: skip that user instead of failing the whole operation.

Example fix

// before
for (const uid of allWorkspaceUserIds) {
  const unread = await getUnreadByUser(roomId, uid); // throws for non-members
}

// after
const members = await this.read.getRoomReader().getMembers(roomId);
for (const member of members) {
  const unread = await getUnreadByUser(roomId, member.id);
}
Defensive patterns

Strategy: validation

Validate before calling

const members = await this.read.getRoomReader().getMembers(roomId);
const isMember = members.some((m) => m.id === uid);
if (!isMember) {
  // skip unread query for this user
}

Type guard

const isMemberOf = (members: Array<{ id: string }>, uid: string): boolean =>
  members.some((m) => m.id === uid);

Try / catch

Catch and degrade: skip this user's unread computation and continue the loop; rethrow only if membership was verified immediately before.

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/46a84d23683fc513. Report an issue: GitHub.

Appendix: source

Thrown at apps/meteor/app/apps/server/bridges/rooms.ts:330

		const users = await Users.findByIds(subs.map((user: { uid: string }) => user.uid)).toArray();
		const userConverter = this.orch.getConverters().get('users');
		return users.map((user: ICoreUser) => userConverter.convertToApp(user));
	}

	protected async getUnreadByUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise<Array<IMessageRaw>> {
		this.orch.debugLog(`The App ${appId} is getting the unread messages for the user: "${uid}" in the room: "${roomId}"`);

		const messageConverter = this.orch.getConverters()?.get('messages');
		if (!messageConverter) {
			throw new Error('Message converter not found');
		}

		const subscription = await Subscriptions.findOneByRoomIdAndUserId(roomId, uid, { projection: { ls: 1 } });

		if (!subscription) {
			const errorMessage = `No subscription found for user with ID "${uid}" in room with ID "${roomId}". This means the user is not subscribed to the room.`;
			this.orch.debugLog(errorMessage);
			throw new Error('User not subscribed to room');
		}

		const lastSeen = subscription?.ls;
		if (!lastSeen) {
			return [];
		}

		const sort: Sort = options.sort?.createdAt ? { ts: options.sort.createdAt } : { ts: 1 };

		const cursor = Messages.findVisibleByRoomIdBetweenTimestampsNotContainingTypes(
			roomId,
			lastSeen,
			new Date(),
			[],
			{
				limit: options.limit,
				skip: options.skip,
				sort,

View on GitHub (pinned to b2c16d5842)