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
- Verify the user exists before enqueueing/invoking role sync, or capture deletion events to cancel pending syncs.
- Make queue handlers idempotent: treat a missing user as a no-op for role synchronization.
- 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
- Order lifecycle operations: cancel pending role syncs when a user is deleted.
- Make queue handlers idempotent so a missing user is a no-op, not a poison message.
- Never reuse user ids across environments; resolve ids per workspace.
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
- error-invalid-roleId
- error-role-not-found
- error-action-not-allowed
- error-action-not-allowed
- error-action-not-allowed
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)