RocketChat/Rocket.Chat · error · MeteorError

error-user-not-found

error-user-not-found

Error message

User not found

What it means

saveUser throws error-user-not-found (method 'saveUser') on the update path: userData has an _id (isUpdateUserData true) but Users.findOneById(userData._id) returned null. The target of the update does not exist; only inserts without _id, or updates against an existing record, are valid.

Source

Thrown at apps/meteor/server/lib/users/saveUser/saveUser.ts:108

		let sendPassword = false;

		if (userData.hasOwnProperty('setRandomPassword')) {
			if (userData.setRandomPassword) {
				userData.password = generatePassword();
				userData.requirePasswordChange = true;
				sendPassword = true;
			}

			delete userData.setRandomPassword;
		}

		if (!isUpdateUserData(userData)) {
			// TODO audit new users
			return saveNewUser(userData, sendPassword, performedBy);
		}

		if (!oldUserData) {
			throw new MeteorError('error-user-not-found', 'User not found', {
				method: 'saveUser',
			});
		}

		options?.auditStore?.setOriginalUser(oldUserData);

		await validateUserEditing(userId, userData);

		// update user
		const updater = Users.getUpdater();

		if (userData.hasOwnProperty('username') || userData.hasOwnProperty('name')) {
			if (
				!(await saveUserIdentity({
					_id: userData._id,
					username: userData.username,
					name: userData.name,
					updateUsernameInBackground: true,

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Look the user up by a stable key (username/email) to resolve the current _id right before saving
  2. Treat this error as a 404 and refresh the client's copy of the user
  3. Never fabricate _ids for updates; omit _id to create, or query it first

Example fix

// before
await saveUser(actorId, { _id, name: 'New Name' }); // stale _id -> error-user-not-found

// after
const existing = await Users.findOneByUsernameIgnoringCase(username);
if (!existing) throw new Error('User no longer exists');
await saveUser(actorId, { _id: existing._id, name: 'New Name' });
Defensive patterns

Strategy: validation

Validate before calling

if (userData._id) {
  const existing = await Users.findOneById(userData._id, { projections: { _id: 1 } });
  if (!existing) throw new Error('Target user no longer exists');
}
await saveUser(actorId, userData);

Type guard

const isUpdateOfExistingUser = async (id: string): Promise<boolean> =>
  !!(await Users.findOneById(id, { projections: { _id: 1 } }));

Try / catch

try {
  await saveUser(actorId, userData);
} catch (e) {
  if (isMeteorErrorCode(e, 'error-user-not-found')) {
  	refreshUserEditor(); // target vanished; reload instead of retrying blindly
  }
}

Prevention

When it happens

Trigger: Calling saveUser with an _id that was deleted, a fabricated ObjectId from tests, or a race where the user is removed between the client loading the edit form and submitting it.

Common situations: Stale edit forms open in a browser while an admin deletes the account; tooling upserting by guessing ids instead of looking them up; cross-environment id confusion (dev id against prod data).

Related errors


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