RocketChat/Rocket.Chat · error · Meteor.Error

error-not-authorized

error-not-authorized

Error message

Not authorized

What it means

The user is authenticated, but hasPermissionAsync(userId, 'view-join-code') returned false, so getRoomJoinCode refuses with 'error-not-authorized'. The view-join-code permission is admin-oriented by default; ordinary members and even room owners typically do not hold it.

Source

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

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. Grant view-join-code to the user's role in Administration -> Permissions
  2. Assign the user a role (e.g. admin) that already holds view-join-code
  3. Have an admin fetch the join code and share it through a controlled channel instead
  4. If the feature is not needed, remove the call - join codes are sensitive room credentials

Example fix

// before - any member UI tries to read the join code
const code = await Meteor.callAsync('getRoomJoinCode', rid);

// after - only render the affordance for users with the permission
const canView = await Meteor.callAsync('getUserPermission', 'view-join-code'); // or track roles client-side
if (!canView) return null;
const code = await Meteor.callAsync('getRoomJoinCode', rid);
Defensive patterns

Strategy: try-catch

Validate before calling

// hide the affordance unless the user's roles plausibly hold the permission
const roles = Meteor.user()?.roles ?? [];
const canProbablyView = roles.includes('admin'); // conservative client-side check
if (!canProbablyView) return null;
const code = await Meteor.callAsync('getRoomJoinCode', rid);

Type guard

const isJoinCode = (v: string | false): v is string => typeof v === 'string';

Try / catch

try {
  const code = await Meteor.callAsync('getRoomJoinCode', rid);
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'error-not-authorized') {
    // permission denied - degrade gracefully instead of surfacing an error
    showJoinCodeUnavailable();
  }
}

Prevention

When it happens

Trigger: A non-admin user calls Meteor.call('getRoomJoinCode', rid); a custom role was created without the view-join-code permission; the permission was revoked from a role after code that depended on it shipped.

Common situations: Apps or UI features surfacing join codes to regular users; permission matrix edits in Administration -> Permissions that dropped view-join-code; enterprise roles provisioned without the admin default set.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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