RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-user

error-invalid-user

Error message

Invalid user

What it means

loadHistory is the only history method with a guest mode: it supports anonymous reads when the workspace setting Accounts_AllowAnonymousRead is enabled. It throws error-invalid-user when the caller has no user record AND Accounts_AllowAnonymousRead === false. If the setting is enabled, anonymous loads proceed to the normal room-access check, which returns false rather than throwing when access is denied.

Source

Thrown at apps/meteor/server/meteor-methods/messages/loadHistory.ts:39

			showThreadMessages?: boolean,
		):
			| {
					messages: IMessage[];
					firstUnread: IMessage | undefined;
					unreadNotLoaded: number;
			  }
			| false;
	}
}

Meteor.methods<ServerMethods>({
	async loadHistory(rid, end, limit = 20, ls, showThreadMessages = true) {
		methodDeprecationLogger.method('loadHistory', '9.0.0', '/v1/rooms.history');
		check(rid, String);
		const fromUser = await Meteor.userAsync();

		if (!fromUser && settings.get('Accounts_AllowAnonymousRead') === false) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'loadHistory',
			});
		}

		const room = await Rooms.findOneById(rid, { projection: { ...roomAccessAttributes, t: 1, sysMes: 1 } });
		if (!room) {
			return false;
		}

		// this checks the Allow Anonymous Read setting, so no need to check again
		if (!(await canAccessRoomAsync(room, fromUser || undefined))) {
			return false;
		}

		// if fromId is undefined and it passed the previous check, the user is reading anonymously
		if (!fromUser) {
			return loadMessageHistory({ rid, end, limit, ls, showThreadMessages, room });
		}

View on GitHub (pinned to b263243745)

Solutions

  1. Authenticate the visitor before loading history
  2. Enable Accounts_AllowAnonymousRead (Administration → Accounts) if guest reading of public channels is intended
  3. Catch the error and redirect anonymous visitors to the login page

Example fix

// before
const result = await Meteor.callAsync('loadHistory', rid, end, limit, ls);

// after (client)
const anonReadAllowed = useSetting('Accounts_AllowAnonymousRead');
if (!Meteor.userId() && !anonReadAllowed) {
  showLoginPrompt();
} else {
  const result = await Meteor.callAsync('loadHistory', rid, end, limit, ls);
}
Defensive patterns

Strategy: validation

Validate before calling

const anonReadAllowed = publicSettings.get('Accounts_AllowAnonymousRead') ?? false;
if (!Meteor.userId() && !anonReadAllowed) {
  // anonymous history is disabled — send the visitor to login
}

Try / catch

try {
  const history = await Meteor.callAsync('loadHistory', rid, end, limit, ls);
} catch (error) {
  if (error instanceof Meteor.Error && error.error === 'error-invalid-user') {
    showLoginPrompt(); // guest read disabled and no session
    return false;
  }
  throw error;
}

Prevention

When it happens

Trigger: A logged-out visitor opening a public channel while Accounts_AllowAnonymousRead is disabled (the default); anonymous crawlers or link previews hitting a workspace where guest read was turned off; DDP clients without a login calling loadHistory.

Common situations: An admin disables Accounts_AllowAnonymousRead after previously allowing guests; bots scraping public channels without credentials; guest sessions continuing after the setting changed.

Understand the failure class

Background: error-invalid-user: "Invalid user" errors in Rocket.Chat — what they mean and how to fix them — this error's family across 2 libraries.

Related errors


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