RocketChat/Rocket.Chat · error · Meteor.Error
error-subscription-not-found
error-subscription-not-found
Error message
Subscription not found
What it means
unreadMessages requires the caller to have a subscription with a last-seen timestamp in the message's room: Subscriptions.findOneByRoomIdAndUserId(rid, userId)?.ls. A missing subscription (or one without ls) throws Meteor.Error('error-subscription-not-found', 'Subscription not found') - without ls the server cannot compute the unread window.
Source
Thrown at apps/meteor/server/lib/messaging/unread/unreadMessages.ts:63
});
}
const originalMessage = await Messages.findOneById(firstUnreadMessage._id, {
projection: {
u: 1,
rid: 1,
ts: 1,
},
});
if (!originalMessage || userId === originalMessage.u._id) {
throw new Meteor.Error('error-action-not-allowed', 'Not allowed', {
method: 'unreadMessages',
action: 'Unread_messages',
});
}
const lastSeen = (await Subscriptions.findOneByRoomIdAndUserId(originalMessage.rid, userId))?.ls;
if (!lastSeen) {
throw new Meteor.Error('error-subscription-not-found', 'Subscription not found', {
method: 'unreadMessages',
action: 'Unread_messages',
});
}
if (originalMessage.ts >= lastSeen) {
return logger.debug('Provided message is already marked as unread');
}
logger.debug({
msg: 'Updating unread message as the first unread',
timestamp: originalMessage.ts,
});
const setAsUnreadResponse = await Subscriptions.setAsUnreadByRoomIdAndUserId(originalMessage.rid, userId, originalMessage.ts);
if (setAsUnreadResponse.modifiedCount) {
void notifyOnSubscriptionChangedByRoomIdAndUserId(originalMessage.rid, userId);
}
};View on GitHub (pinned to b2c16d5842)
Solutions
- Ensure the user joined the room (subscription exists) before enabling mark-unread
- Refresh subscriptions after kick/remove events and hide such rooms
- Join the room first when acting on previewed public channels
Example fix
// before
Meteor.call('unreadMessages', { _id: messageId }); // not a member
// after
const sub = Subscriptions.findOne({ rid: message.rid });
if (sub) {
Meteor.call('unreadMessages', { _id: messageId });
} else {
Meteor.call('joinRoom', message.rid); Defensive patterns
Strategy: validation
Validate before calling
const sub = await Subscriptions.findOneByRoomIdAndUserId(message.rid, userId);
if (!sub?.ls) {
return; // no membership/last-seen: unread is not applicable
}
await unreadMessages(userId, { _id: message._id }); Try / catch
Meteor.call('unreadMessages', { _id }, undefined, (err) => {
if (err?.error === 'error-subscription-not-found') {
hideRoomAndResync(message.rid); // user is not a member anymore
}
}); Prevention
- Gate unread actions on a locally cached subscription that has an ls timestamp
- After kick/remove stream events, drop unread affordances for that room
- Join before acting when interacting with previewed public channels
When it happens
Trigger: Marking a message unread in a room the user has no subscription for: never joined, was removed, or only previewed a public channel without joining; calling with a message id belonging to a room the account cannot see.
Common situations: Stale UI offering unread for rooms the user was removed from; directory/preview flows where the room is visible but not joined; race between joining and acting.
Related errors
- error-no-message-for-unread
- error-action-not-allowed
- error-invalid-user
- User not subscribed to room
- error-federated-users-in-non-federated-rooms
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/23447221f8a1e2b6.
Report an issue: GitHub.