RocketChat/Rocket.Chat · error · Meteor.Error
error-empty-invite-list
error-empty-invite-list
Error message
Cannot invite if no valid users are provided
What it means
Thrown by POST groups.invite (non-federated branch) when getUserListFromParams(this.bodyParams) resolves to zero users. The helper looks for user identifiers in the body (userId / username / users arrays); if none of the provided identifiers match an existing user account, the list is empty and the invite is rejected before addUsersToRoomMethod runs. Federated rooms skip this path and invite by raw username.
Solutions
- Verify each invitee exists first via users/info (username or userId) and drop unknown ones from the list
- Use the exact body field names the endpoint reads - userId/username singular, or the array forms - check your SDK version's typings
- For not-yet-provisioned users, create the account (or wait for SSO provisioning) before inviting
Example fix
// before
await sdk.post('groups.invite', { roomId, users: ['alice', 'ghost-user'] }); // ghost-user unknown -> empty after filter
// after
const invitees = [];
for (const u of ['alice', 'ghost-user']) {
try { await sdk.get('users.info', { username: u }); invitees.push(u); } catch { /* log skip */ }
}
if (invitees.length) await sdk.post('groups.invite', { roomId, users: invitees }); Defensive patterns
Strategy: validation
Validate before calling
async function resolveInvitees(usernames) {
const found = [];
for (const username of usernames) {
try { await sdk.get('users/info', { username }); found.push({ username }); }
catch (e) { logger.warn(`skipping unknown user ${username}`); }
}
if (!found.length) throw new Error('no existing users to invite');
return found;
} Type guard
const isNonEmptyUserList = (users: unknown): users is { username: string }[] =>
Array.isArray(users) && users.length > 0 && users.every((u) => typeof u?.username === 'string' || typeof u?.userId === 'string'); Try / catch
try { await sdk.post('groups.invite', { roomId, users }); } catch (e) {
if (e.error === 'error-empty-invite-list') { /* re-validate users via users/info, fix field names, drop ghosts */ }
} Prevention
- Pre-resolve invitees with users/info and invite only confirmed accounts
- Use the exact field names your server version reads (userId/username/users) - check rest-typings
- For SSO-provisioned users, order the pipeline: create/wait-for account, then invite
When it happens
Trigger: POST groups.invite { roomId, username: 'deleted.user' }; passing usernames of deactivated users; sending the wrong field name (e.g. 'userIds' when the API expects 'users'/'userId'); typo'd usernames; LDAP-synced usernames with different casing/spelling than assumed.
Common situations: Inviting users before their accounts exist (SSO provisioning lag); mixing userId and username field conventions between SDK versions; deactivated users lingering in onboarding lists.
Related errors
- error-not-allowed
- error-room-archived
- error-room-not-found
- error-room-param-not-provided
- agent-not-found
AI-assisted analysis of RocketChat/Rocket.Chat@e4b8178b20 (2026-08-18).
Data as JSON: /api/errors/663a3ee3bdddf8ad.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/api/v1/groups.ts:872
const groupRoom = await Rooms.findOneByIdOrName(idOrName);
const { _id: rid, t: type } = groupRoom || {};
if (!rid || type !== 'p') {
throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "roomName" param provided does not match any group');
}
// Federated rooms invite by raw username: the federated user record is created
// lazily inside addUsersToRoomMethod, so we must not require it to exist locally yet.
if (groupRoom && isRoomNativeFederated(groupRoom)) {
const usernames = await getUsernameListFromParams(this.bodyParams);
await addUsersToRoomMethod(this.userId, { rid, users: usernames }, this.user);
} else {
const users = await getUserListFromParams(this.bodyParams);
if (!users.length) {
throw new Meteor.Error('error-empty-invite-list', 'Cannot invite if no valid users are provided');
}
await addUsersToRoomMethod(this.userId, { rid, users: users.map((u) => u.username).filter(isTruthy) }, this.user);
}
const room = await Rooms.findOneById(rid, { projection: API.v1.defaultFieldsToExclude });
if (!room) {
throw new Meteor.Error('error-room-not-found', 'The required "roomId" or "roomName" param provided does not match any group');
}
return API.v1.success({
group: await composeRoomWithLastMessage(room, this.userId),
});
},
);
API.v1.post(View on GitHub (pinned to e4b8178b20)