RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-user
error-invalid-user
Error message
Invalid user
What it means
readMessages marks a room (and optionally its threads) as read and is strictly per-user: Meteor.userId() returning null throws error-invalid-user before any room lookup. Read receipts and unread counters are keyed to the logged-in user, so an anonymous call is meaningless and rejected.
Source
Thrown at apps/meteor/server/meteor-methods/messages/readMessages.ts:23
import { Meteor } from 'meteor/meteor';
import { canAccessRoomAsync } from '../../lib/authorization';
import { readMessages } from '../../lib/readMessages';
declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
readMessages(rid: string, readThreads?: boolean): Promise<void>;
}
}
Meteor.methods<ServerMethods>({
async readMessages(rid, readThreads = false) {
check(rid, String);
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'readMessages',
});
}
const user = ((await Meteor.userAsync()) as IUser | null) ?? undefined;
const room = await Rooms.findOneById(rid);
if (!room) {
throw new Meteor.Error('error-room-does-not-exist', 'This room does not exist', { method: 'readMessages' });
}
if (!(await canAccessRoomAsync(room, user))) {
throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'readMessages' });
}
await readMessages(room, userId, readThreads);
},
});
View on GitHub (pinned to b2c16d5842)
Solutions
- Check Meteor.userId() before calling and skip when logged out
- Cancel pending read calls on logout (cleanup in component unmount / auth-change handlers)
- Re-authenticate and let the normal flow re-mark the room read
Example fix
// before
useEffect(() => {
Meteor.call('readMessages', rid);
}, [rid]);
// after
useEffect(() => {
if (!Meteor.userId()) return;
Meteor.call('readMessages', rid);
}, [rid]); Defensive patterns
Strategy: validation
Validate before calling
if (!Meteor.userId()) {
// marking as read requires a logged-in user — skip the call
} Try / catch
try {
await Meteor.callAsync('readMessages', rid, readThreads);
} catch (error) {
if (error instanceof Meteor.Error && error.error === 'error-invalid-user') {
// ignore: no session — the next login will re-mark rooms read
} else {
throw error;
}
} Prevention
- Guard mark-as-read effects with Meteor.userId()
- Cancel pending read calls on logout/unmount
- Fire read calls only after the room is actually visible to an authenticated user
When it happens
Trigger: Meteor.call('readMessages', rid, readThreads) fired by an on-mount effect in a logged-out state, after logout while the room component was still mounted, or from an unauthenticated DDP client.
Common situations: Mark-as-read effects racing logout; components that keep firing read calls after the session expired; automation scripts trying to clear unread flags without logging in.
Related errors
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/6521a3e091236dbb.
Report an issue: GitHub.