RocketChat/Rocket.Chat · warning · Meteor.Error
error-no-message-for-unread
error-no-message-for-unread
Error message
There are no messages to mark unread
What it means
When unreadMessages is called with a room id, it fetches the newest visible message via findVisibleByRoomId (limit 1, ts desc); if none exists it throws Meteor.Error('error-no-message-for-unread', 'There are no messages to mark unread'). The room has zero messages eligible for an unread marker - empty room, or every message invisible (deleted/hidden).
Source
Thrown at apps/meteor/server/lib/messaging/unread/unreadMessages.ts:27
declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
unreadMessages(firstUnreadMessage?: Pick<IMessage, '_id'>, room?: IRoom['_id']): void;
}
}
export const unreadMessages = async (userId: string, firstUnreadMessage?: Pick<IMessage, '_id'>, room?: IRoom['_id']): Promise<void> => {
if (room && typeof room === 'string') {
const lastMessage = (
await Messages.findVisibleByRoomId(room, {
limit: 1,
sort: { ts: -1 },
}).toArray()
)[0];
if (!lastMessage) {
throw new Meteor.Error('error-no-message-for-unread', 'There are no messages to mark unread', {
method: 'unreadMessages',
action: 'Unread_messages',
});
}
const setAsUnreadResponse = await Subscriptions.setAsUnreadByRoomIdAndUserId(lastMessage.rid, userId, lastMessage.ts);
if (setAsUnreadResponse.modifiedCount) {
void notifyOnSubscriptionChangedByRoomIdAndUserId(lastMessage.rid, userId);
}
return;
}
if (typeof firstUnreadMessage?._id !== 'string') {
throw new Meteor.Error('error-action-not-allowed', 'Not allowed', {
method: 'unreadMessages',
action: 'Unread_messages',
});View on GitHub (pinned to b2c16d5842)
Solutions
- Client: gate the mark-unread action on room.lastMessage being present
- If messages were purged, acknowledge the room (mark read) instead of marking unread
- Skip the call when the local room cache shows no visible history
Example fix
// before
Meteor.call('unreadMessages', null, rid); // throws on empty rooms
// after
const room = Rooms.findOne({ _id: rid });
if (room?.lastMessage) {
Meteor.call('unreadMessages', null, rid); Defensive patterns
Strategy: validation
Validate before calling
// The rooms stream carries lastMessage - use it as the precondition
const room = Rooms.findOne({ _id: rid });
if (room?.lastMessage) {
Meteor.call('unreadMessages', null, rid);
} else {
markRoomAsRead(rid); // nothing to unread
} Try / catch
Meteor.call('unreadMessages', null, rid, (err) => {
if (err?.error === 'error-no-message-for-unread') {
markRoomAsRead(rid); // benign - clear any unread badge and move on
}
}); Prevention
- Drive mark-unread availability from room.lastMessage
- After retention purges, expect this error and fall back to read state
- Do not queue unread calls for rooms whose history has not synced yet
When it happens
Trigger: Meteor.call('unreadMessages', undefined, roomId) on a room with no visible messages: a freshly created empty channel, a room whose only messages were deleted, or a retention purge that removed everything.
Common situations: Mark-as-unread offered on empty rooms; messages purged while the room was already read; UI not disabling the unread action when lastMessage is absent.
Related errors
- error-action-not-allowed
- error-subscription-not-found
- error-invalid-user
- You can't send messages because the room is readonly.
- error-invalid-user
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/228682d2588fa277.
Report an issue: GitHub.