RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-user

error-invalid-user

Error message

Invalid user

What it means

loadNextMessages requires an authenticated caller: Meteor.userId() returning null throws error-invalid-user before rid or room access is evaluated. The method fetches messages following a given timestamp and has no anonymous mode (unlike loadHistory).

Source

Thrown at apps/meteor/server/meteor-methods/messages/loadNextMessages.ts:23

import { Meteor } from 'meteor/meteor';

import { canAccessRoomIdAsync } from '../../lib/authorization/canAccessRoom';
import { normalizeMessagesForUser } from '../../lib/utils/lib/normalizeMessagesForUser';

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		loadNextMessages(rid: IRoom['_id'], end?: Date, limit?: number): Promise<{ messages: IMessage[] }>;
	}
}

Meteor.methods<ServerMethods>({
	async loadNextMessages(rid, end, limit = 20) {
		check(rid, String);
		check(limit, Number);

		if (!Meteor.userId()) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'loadNextMessages',
			});
		}

		if (!rid) {
			throw new Meteor.Error('error-invalid-room', 'Invalid room', { method: 'loadNextMessages' });
		}

		const fromId = Meteor.userId();

		if (!fromId || !(await canAccessRoomIdAsync(rid, fromId))) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'loadNextMessages' });
		}

		let records;
		if (end) {
			records = await Messages.findVisibleByRoomIdAfterTimestamp(rid, end, true, {
				sort: {

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Check Meteor.userId() before calling and gate the UI on login state
  2. Re-authenticate on session expiry, then retry the fetch
  3. For scripts, log in over DDP before invoking the method

Example fix

// before
const { messages } = await Meteor.callAsync('loadNextMessages', rid, end, limit);

// after
if (!Meteor.userId()) {
  // require login before paging newer messages
} else {
  const { messages } = await Meteor.callAsync('loadNextMessages', rid, end, limit);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!Meteor.userId()) {
  // loadNextMessages requires a logged-in user
}

Try / catch

try {
  const { messages } = await Meteor.callAsync('loadNextMessages', rid, end, limit);
} catch (error) {
  if (error instanceof Meteor.Error && error.error === 'error-invalid-user') {
    // session expired — re-authenticate and retry once
  } else {
    throw error;
  }
}

Prevention

When it happens

Trigger: Meteor.call('loadNextMessages', rid, end, limit) from a logged-out tab, with an invalidated resume token, or from a DDP client that never performed a login.

Common situations: Expired sessions in long-lived tabs; 'load more' buttons firing after logout; automation scripts calling the method without authenticating.

Related errors


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