RocketChat/Rocket.Chat · warning · Meteor.Error

error-invalid-user

error-invalid-user

Error message

Invalid user to delete

What it means

`executeDeleteUser` (the core of the `deleteUser` Meteor method and REST `users.delete`) loads the target with `Users.findOneById(userId)` and throws `error-invalid-user` ('Invalid user to delete') when no user document matches. The check runs after caller permission checks but before any protection logic (app users, last admin), so nothing is deleted and no side effects occur.

Source

Thrown at apps/meteor/server/meteor-methods/users/deleteUser.ts:21

import { Users } from '@rocket.chat/models';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';

import { hasPermissionAsync } from '../../lib/authorization/hasPermission';
import { methodDeprecationLogger } from '../../lib/deprecationWarningLogger';
import { deleteUser } from '../../lib/users/deleteUser';

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		deleteUser(userId: IUser['_id'], confirmRelinquish?: boolean): boolean;
	}
}

export const executeDeleteUser = async (fromUserId: IUser['_id'], userId: IUser['_id'], confirmRelinquish = false): Promise<boolean> => {
	const user = await Users.findOneById(userId);
	if (!user) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user to delete', {
			method: 'deleteUser',
		});
	}

	if (user.type === 'app') {
		throw new Meteor.Error('error-cannot-delete-app-user', 'Deleting app user is not allowed', {
			method: 'deleteUser',
		});
	}

	const adminCount = await Users.countDocuments({ roles: 'admin' });

	const userIsAdmin = user.roles?.indexOf('admin') > -1;

	if (adminCount === 1 && userIsAdmin) {
		throw new Meteor.Error('error-action-not-allowed', 'Leaving the app without admins is not allowed', {
			method: 'deleteUser',
			action: 'Remove_last_admin',

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Verify the user exists first (`GET /api/v1/users.info?userId=<id>`) before issuing the delete.
  2. In sync/automation scripts, treat `error-invalid-user` on delete as idempotent success.
  3. Refresh the user list after a successful delete and avoid reusing captured ids.
  4. Double-check environment (workspace) when copying ids between instances.
Defensive patterns

Strategy: validation

Validate before calling

// confirm the target exists before deleting
const res = await fetch(`/api/v1/users.info?userId=${encodeURIComponent(userId)}`, { headers: authHeaders });
if (res.ok) {
  await deleteUserFn(uid, userId, confirmRelinquish);
}

Type guard

const isExistingUser = (users: { _id: string }[], userId: string): boolean => users.some((u) => u._id === userId);

Try / catch

try {
  await Meteor.callAsync('deleteUser', userId, confirmRelinquish);
} catch (e: any) {
  if (e?.error === 'error-invalid-user' && e?.reason === 'Invalid user to delete') {
    return true; // already gone - treat delete as done (idempotent)
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `deleteUser(userId, confirmRelinquish?)` with an id that does not exist or was already deleted: double-click on a delete button, stale user list in an admin table, retrying a delete that already succeeded, or a malformed/copied id.

Common situations: Two admins deleting the same user concurrently; UI not refreshing after deletion; scripts replaying deletions against a workspace where the user is gone; ids from one environment used against another.

Related errors


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