RocketChat/Rocket.Chat · warning · Meteor.Error
error-user-already-owner
error-user-already-owner
Error message
User is already an owner
What it means
Thrown by addRoomOwner() in apps/meteor/server/meteor-methods/rooms/addRoomOwner.ts:64 when the target's subscription already has 'owner' in its roles array. The check is explicit (Array.isArray + includes) and acts as an idempotency guard so the role is not re-added, no system message is sent, and no federation event fires for a no-op.
Source
Thrown at apps/meteor/server/meteor-methods/rooms/addRoomOwner.ts:64
const user = await Users.findOneById(userId);
if (!user?.username) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'addRoomOwner',
});
}
const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id);
if (!subscription) {
throw new Meteor.Error('error-user-not-in-room', 'User is not in this room', {
method: 'addRoomOwner',
});
}
if (subscription.roles && Array.isArray(subscription.roles) === true && subscription.roles.includes('owner') === true) {
throw new Meteor.Error('error-user-already-owner', 'User is already an owner', {
method: 'addRoomOwner',
});
}
await beforeChangeRoomRole.run({ fromUserId, userId, room, role: 'owner' });
const addRoleResponse = await Subscriptions.addRoleById(subscription._id, 'owner');
await syncRoomRolePriorityForUserAndRoom(userId, rid, subscription.roles?.concat(['owner']) || ['owner']);
if (addRoleResponse.modifiedCount) {
void notifyOnSubscriptionChangedById(subscription._id);
}
const fromUser = await Users.findOneById(fromUserId);
if (!fromUser) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'addRoomLeader',
});View on GitHub (pinned to b2c16d5842)
Solutions
- Treat this error as benign if it comes from a retry: verify current roles and skip instead of re-calling.
- Pre-check before calling: fetch the subscription and return early when subscription.roles?.includes('owner').
- Make client actions idempotent (disable the button while in-flight) so the second call never happens.
- For sync jobs, reconcile by diffing current roles against desired roles rather than blindly applying.
Example fix
// before
await addRoomOwner(uid, rid, targetUserId); // may throw error-user-already-owner
// after
const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, targetUserId, { projection: { roles: 1 } });
if (sub?.roles?.includes('owner')) return; // already owner, no-op
await addRoomOwner(uid, rid, targetUserId); Defensive patterns
Strategy: type-guard
Validate before calling
const sub = await Subscriptions.findOneByRoomIdAndUserId(rid, userId, { projection: { roles: 1 } });
if (sub?.roles?.includes('owner')) return; // already owner: skip instead of throwing Type guard
const isAlreadyOwner = (sub: { roles?: string[] } | null): boolean =>
Array.isArray(sub?.roles) === true && sub!.roles!.includes('owner'); Try / catch
try {
await addRoomOwner(uid, rid, userId);
} catch (e) {
if (e instanceof Meteor.Error && e.error === 'error-user-already-owner') {
// benign no-op: treat as success in idempotent flows
return true;
}
throw e;
} Prevention
- Make promote actions idempotent: pre-read subscription.roles and short-circuit.
- Disable submit buttons while a role change is in flight to prevent double calls.
- In retry logic, map 'error-user-already-owner' to success instead of surfacing an error.
When it happens
Trigger: Calling addRoomOwner twice for the same user; retrying after a timeout when the first call actually succeeded; UI double-submission; scripts syncing roles that do not read the current subscription.roles before writing; a previous partial run added the role and then the method was re-invoked.
Common situations: Retry logic without idempotency checks after network flaps; bulk role-sync jobs replaying their whole list; two admins promoting the same person simultaneously; client re-render triggering a duplicate method call.
Related errors
- error-room-e2e-key-already-exists
- error-user-already-leader
- error-user-already-moderator
- error-user-already-in-role
- error-user-not-in-role
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/76e126b54a3d5dc3.
Report an issue: GitHub.