RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-arguments

error-invalid-arguments

Error message

Invalid arguments

What it means

Thrown by addUsersToRoomMethod() in apps/meteor/server/meteor-methods/rooms/addUsersToRoom.ts:81 when Array.isArray(data.users) is false. The users payload must be an array of usernames; the method then maps over it (data.users.map) in a Promise.all, so a non-array would crash the loop if not rejected here. Note this check runs after the permission checks, so an unauthorized caller gets error-not-allowed first.

Source

Thrown at apps/meteor/server/meteor-methods/rooms/addUsersToRoom.ts:81

	let canAddUser = false;
	if (userInRoom && (await hasPermissionAsync(userId, 'add-user-to-joined-room', room._id))) {
		canAddUser = true;
	} else if (room.t === 'c' && (await hasPermissionAsync(userId, 'add-user-to-any-c-room'))) {
		canAddUser = true;
	} else if (room.t === 'p' && (await hasPermissionAsync(userId, 'add-user-to-any-p-room'))) {
		canAddUser = true;
	}

	// Adding wasn't allowed
	if (!canAddUser) {
		throw new Meteor.Error('error-not-allowed', 'Not allowed', {
			method: 'addUsersToRoom',
		});
	}

	// Missing the users to be added
	if (!Array.isArray(data.users)) {
		throw new Meteor.Error('error-invalid-arguments', 'Invalid arguments', {
			method: 'addUsersToRoom',
		});
	}

	await beforeAddUsersToRoom.run({ usernames: data.users, inviter: user }, room);

	await Promise.all(
		data.users.map(async (username) => {
			const sanitizedUsername = sanitizeUsername(username);

			const newUser = await Users.findOneByUsernameIgnoringCase(sanitizedUsername);
			if (!newUser) {
				throw new Meteor.Error('error-user-not-found', 'User not found', {
					method: 'addUsersToRoom',
				});
			}

			const subscription = await Subscriptions.findOneByRoomIdAndUserId(data.rid, newUser._id);

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Normalize to an array at the boundary: const users = Array.isArray(x) ? x : [x].filter(Boolean).
  2. Validate the payload shape before calling (Match/Object schema or a manual typeof check).
  3. If porting REST logic, remember the method takes usernames (strings), not user IDs or user objects.
  4. Type the payload as { rid: string; users: string[] } at your call site so the compiler catches scalar mistakes.

Example fix

// before
await addUsersToRoomMethod(uid, { rid, users: payload.username });

// after
const users = Array.isArray(payload.users) ? payload.users : [payload.username].filter(Boolean);
await addUsersToRoomMethod(uid, { rid, users });
Defensive patterns

Strategy: type-guard

Validate before calling

if (!Array.isArray(data.users)) {
  data.users = [data.users].filter(Boolean);
}

Type guard

const isUsernameArray = (v: unknown): v is string[] =>
  Array.isArray(v) && v.every((x) => typeof x === 'string');

Try / catch

try {
  await addUsersToRoomMethod(uid, { rid, users });
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'error-invalid-arguments') {
    // normalize users to string[] and retry once
  }
}

Prevention

When it happens

Trigger: Passing data.users as a single username string, undefined (key missing), null, or an object; API shims mapping a REST body {username} directly to {users}; JSON inputs where users is an object map rather than an array.

Common situations: Client refactors renaming the field; type drift between callers written in JS and the TS signature; wrapping/unwrapping payloads incorrectly in automation scripts; the single-user 'addUserToRoom' wrapper being bypassed and the bulk method called with a scalar.

Related errors


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