RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-user

error-invalid-user

Error message

Invalid user

What it means

After the emoji check passes, executeSetReaction loads the acting user with Users.findOneById(userId) and throws Meteor.Error('error-invalid-user', 'Invalid user') when no user document matches. The method invocation carried a userId that does not resolve to a persisted account: deleted user, corrupted id, or an unauthenticated caller.

Source

Thrown at apps/meteor/server/lib/messaging/reactions/setReaction.ts:114

export async function executeSetReaction(
	userId: string,
	reaction: string,
	messageParam: IMessage['_id'] | IMessage,
	shouldReact?: boolean,
) {
	// Check if the emoji is valid before proceeding
	const reactionWithoutColons = reaction.replace(/:/g, '');
	reaction = `:${reactionWithoutColons}:`;

	if (!emoji.list[reaction] && (await EmojiCustom.countByNameOrAlias(reactionWithoutColons)) === 0) {
		throw new Meteor.Error('error-not-allowed', 'Invalid emoji provided.', {
			method: 'setReaction',
		});
	}

	const user = await Users.findOneById(userId);
	if (!user) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'setReaction' });
	}

	const message = typeof messageParam === 'string' ? await Messages.findOneById(messageParam) : messageParam;
	if (!message) {
		throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'setReaction' });
	}

	const userAlreadyReacted = Boolean(message.reactions?.[reaction]?.usernames?.includes(user.username as string));

	// When shouldReact was not informed, toggle the reaction.
	if (shouldReact === undefined) {
		shouldReact = !userAlreadyReacted;
	}

	if (userAlreadyReacted === shouldReact) {
		return;
	}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Client: treat it as an invalid session - log out and re-authenticate so this.userId refreshes
  2. Server code: verify Users.findOneById(userId) exists before delegating to executeSetReaction
  3. In tests, insert the user document (or stub the model) before invoking the reaction flow

Example fix

// before (test/helper invoking the internal API)
await executeSetReaction('missing-user-id', 'tada', messageId); // throws error-invalid-user

// after
const user = await Users.findOneById(userId, { projections: { _id: 1 } });
if (!user) throw new Meteor.Error('error-invalid-user', 'Invalid user');
await executeSetReaction(user._id, 'tada', messageId);
Defensive patterns

Strategy: try-catch

Validate before calling

// Server-side wrapper: confirm the user exists before delegating
const user = await Users.findOneById(userId, { projections: { _id: 1 } });
if (!user) {
  return handleInvalidSession(userId);
}
await executeSetReaction(userId, reaction, messageId);

Type guard

const isExistingUserId = async (id: string): Promise<boolean> =>
  Boolean(await Users.findOneById(id, { projections: { _id: 1 } }));

Try / catch

Meteor.call('setReaction', 'tada', messageId, (err) => {
  if (err?.error === 'error-invalid-user') {
    Meteor.logout(); // local session points at a deleted account - force re-auth
  }
});

Prevention

When it happens

Trigger: A 'setReaction' DDP invocation whose bound this.userId points to a deleted or nonexistent user (account removed while the socket stayed open); server code calling executeSetReaction('bad-id', ...); tests invoking it with fabricated ids that were never inserted.

Common situations: Account deleted/deactivated mid-session with the DDP connection reused; database migrations leaving dangling user ids; unit tests using mock ids without seeding the Users collection.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


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