RocketChat/Rocket.Chat · error · Error

error-user-not-found

Error message

error-user-not-found

What it means

syncUserRoles loads the target with Users.findOneById(uid, { projection: { username: 1, roles: 1 } }); if the document is gone it throws Error('error-user-not-found'). All subsequent logic - role diffing, the guest/license check, add/remove operations - happens only after this lookup succeeds.

Solutions

  1. Verify the user exists before enqueueing/invoking role sync, or capture deletion events to cancel pending syncs.
  2. Make queue handlers idempotent: treat a missing user as a no-op for role synchronization.
  3. Remove stale references to deleted user ids from jobs, scripts, and external systems.

Example fix

// before
await syncUserRoles(uid, newRoles, opts); // throws error-user-not-found for deleted users

// after
const user = await Users.findOneById(uid, { projection: { _id: 1 } });
if (!user) return; // user gone; role sync is moot
await syncUserRoles(uid, newRoles, opts);
Defensive patterns

Strategy: validation

Validate before calling

const user = await Users.findOneById(uid, { projection: { _id: 1 } });
if (!user) {
	// user deleted or wrong id; skip the sync instead of calling syncUserRoles
}

Try / catch

try {
	await syncUserRoles(uid, newRoleList, opts);
} catch (e: any) {
	if (e?.message === 'error-user-not-found') { dropStaleJob(uid); return; } // no user left to sync
	throw e;
}

Prevention

When it happens

Trigger: Calling syncUserRoles with a uid for a user that was deleted (deletion raced the role sync), a malformed uid, or an id that belongs to a different environment/workspace than the database being written.

Common situations: User lifecycle automation enqueueing delete and role-sync in the wrong order; imports referencing user ids that do not exist locally; ids copy-pasted between staging and production.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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

Appendix: source

Thrown at apps/meteor/ee/server/lib/syncUserRoles.ts:63

		void api.broadcast('user.roleUpdate', {
			type,
			_id: roleId,
			u: {
				_id,
				username,
			},
		});
	}
}

export async function syncUserRoles(
	uid: IUser['_id'],
	newRoleList: Array<IRole['_id']>,
	{ allowedRoles, skipRemovingRoles, scope }: setUserRolesOptions,
): Promise<void> {
	const user = await Users.findOneById<Pick<IUser, '_id' | 'username' | 'roles'>>(uid, { projection: { username: 1, roles: 1 } });
	if (!user) {
		throw new Error('error-user-not-found');
	}

	const existingRoles = user.roles;
	const rolesToAdd = filterRoleList(newRoleList, existingRoles, allowedRoles);
	const rolesToRemove = filterRoleList(existingRoles, newRoleList, allowedRoles);

	if (!rolesToAdd.length && !rolesToRemove.length) {
		return;
	}

	const wasGuest = existingRoles.length === 1 && existingRoles[0] === 'guest';
	if (wasGuest && (await License.shouldPreventAction('activeUsers'))) {
		throw new Error('error-license-user-limit-reached');
	}

	if (rolesToAdd.length && (await addUserRolesAsync(uid, rolesToAdd, scope))) {
		broadcastRoleChange('added', rolesToAdd, user);
	}

View on GitHub (pinned to b2c16d5842)