RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-user

error-invalid-user

Error message

Invalid user

What it means

Thrown by the deprecated getRoomJoinCode Meteor method when the connection has no authenticated user. The check is Meteor.userId() based, so it fires for never-logged-in, logged-out, and expired-token connections alike; the returned join code is only ever exposed to identified users.

Source

Thrown at apps/meteor/server/meteor-methods/rooms/getRoomJoinCode.ts:23

import { Meteor } from 'meteor/meteor';

import { hasPermissionAsync } from '../../lib/authorization/hasPermission';

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		getRoomJoinCode(rid: string): string | false;
	}
}
/* @deprecated */
Meteor.methods<ServerMethods>({
	async getRoomJoinCode(rid) {
		check(rid, String);

		const userId = Meteor.userId();

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

		if (!(await hasPermissionAsync(userId, 'view-join-code'))) {
			throw new Meteor.Error('error-not-authorized', 'Not authorized', { method: 'getJoinCode' });
		}

		const room = await Rooms.findById(rid);

		// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
		return Boolean(room) && (isRoomWithJoinCode(room!) ? room.joinCode : false);
	},
});

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Guard the call with Meteor.userId() and defer it until login completes
  2. Re-login if the resume token expired
  3. Prefer room.join with the join code via the REST API (/v1/rooms.join) instead of exposing join codes through DDP

Example fix

// before
const code = await Meteor.callAsync('getRoomJoinCode', rid);

// after
if (!Meteor.userId()) throw new Error('login required');
const code = await Meteor.callAsync('getRoomJoinCode', rid);
Defensive patterns

Strategy: validation

Validate before calling

if (!Meteor.userId()) {
  throw new Error('login required');
}
const code = await Meteor.callAsync('getRoomJoinCode', rid);

Type guard

// the method returns string | false
const isJoinCode = (v: string | false): v is string => typeof v === 'string';
const code = await Meteor.callAsync('getRoomJoinCode', rid);
if (isJoinCode(code)) displayCode(code);

Try / catch

try {
  const code = await Meteor.callAsync('getRoomJoinCode', rid);
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'error-invalid-user') {
    showLoginScreen();
  } else if (e instanceof Meteor.Error && e.error === 'error-not-authorized') {
    hideJoinCodeUI(); // handled at 1488
  }
}

Prevention

When it happens

Trigger: Meteor.call('getRoomJoinCode', rid) from an anonymous DDP connection, e.g. pre-login UI code trying to display a join code, or a logged-out tab still running reactive logic.

Common situations: Onboarding screens that render before Accounts login finishes; sessions invalidated by server restart or token purge; test harnesses calling the method without a user.

Related errors


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