RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-user

error-invalid-user

Error message

Invalid user

What it means

First guard in the exported createChannelMethod helper: the userId argument itself is falsy. The createChannel DDP wrapper resolves and checks Meteor.userId() on its own, so this specific throw is for internal/server-side callers that passed undefined/null/'' as the user id.

Source

Thrown at apps/meteor/server/meteor-methods/rooms/createChannel.ts:36

			customFields?: Record<string, any>,
			extraData?: Record<string, any>,
		): ICreatedRoom;
	}
}

export const createChannelMethod = async (
	userId: string,
	name: string,
	members: string[],
	readOnly = false,
	customFields?: Record<string, any>,
	extraData: Record<string, any> = {},
	excludeSelf = false,
) => {
	check(name, String);
	check(members, Match.Optional([String]));
	if (!userId) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'createChannel' });
	}

	const user = await Users.findOneById(userId, { projection: { services: 0 } });
	if (!user?.username) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'createChannel' });
	}

	if (extraData.teamId) {
		const team = await Team.findOneById<Pick<ITeam, '_id' | 'roomId'>>(extraData.teamId, { projection: { roomId: 1 } });
		if (!team) {
			throw new Meteor.Error('error-team-not-found', 'The "teamId" param provided does not match any team', { method: 'createChannel' });
		}
		if (!(await hasPermissionAsync(userId, 'create-team-channel', team.roomId))) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'createChannel' });
		}
	} else if (!(await hasPermissionAsync(userId, 'create-c'))) {
		throw new Meteor.Error('error-not-allowed', 'Not allowed', { method: 'createChannel' });
	}

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Pass an explicit, existing user _id as the first argument.
  2. Capture Meteor.userId() inside the request context and propagate it into async continuations that call the helper.
  3. Where no human actor exists, use a designated admin/system account or call the createRoom service directly.

Example fix

// before
createChannelMethod(Meteor.userId(), name, members); // null outside a request

// after
const uid = Meteor.userId();
if (!uid) {
  throw new Error('createChannelMethod requires a user id');
}
createChannelMethod(uid, name, members);
Defensive patterns

Strategy: validation

Validate before calling

if (!uid) {
  throw new Error('createChannelMethod requires a real user id');
}
await createChannelMethod(uid, name, members);

Type guard

const isNonEmptyUserId = (uid: string | null | undefined): uid is string =>
  typeof uid === 'string' && uid.length > 0;

Try / catch

try {
  await createChannelMethod(uid, name, members);
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'error-invalid-user') {
    // internal caller bug: uid was falsy - fix the call site, do not retry
    throw new Error('createChannelMethod called without a user id');
  }
  throw e;
}

Prevention

When it happens

Trigger: Custom server code imports createChannelMethod and passes a uid variable that is undefined (Meteor.userId() read outside a request, wrong destructuring, or an argument-order mistake in the long positional signature name/members/readOnly/customFields/extraData/excludeSelf).

Common situations: Logic ported into workers/jobs where no user context exists; refactors that drop or shift arguments; code copied from the wrapper with uid forgotten.

Related errors


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