RocketChat/Rocket.Chat · error · Meteor.Error
error-user-not-owner
error-user-not-owner
Error message
User is not an owner
What it means
removeRoomOwner requires the target's subscription.roles to be an array that includes 'owner'; otherwise it throws error-user-not-owner. Unlike the moderator check, this one also fires when roles is undefined or missing entirely.
Source
Thrown at apps/meteor/server/meteor-methods/rooms/removeRoomOwner.ts:55
}
const user = await Users.findOneById(userId);
if (!user?.username) {
throw new Meteor.Error('error-invalid-user', 'Invalid user', {
method: 'removeRoomOwner',
});
}
const subscription = await Subscriptions.findOneByRoomIdAndUserId(rid, user._id);
if (!subscription) {
throw new Meteor.Error('error-invalid-room', 'Invalid room', {
method: 'removeRoomOwner',
});
}
if (Array.isArray(subscription.roles) === false || subscription.roles?.includes('owner') === false) {
throw new Meteor.Error('error-user-not-owner', 'User is not an owner', {
method: 'removeRoomOwner',
});
}
const numOwners = await Roles.countUsersInRole('owner', rid);
if (numOwners === 1) {
throw new Meteor.Error('error-remove-last-owner', 'This is the last owner. Please set a new owner before removing this one.', {
method: 'removeRoomOwner',
});
}
await beforeChangeRoomRole.run({ fromUserId, userId, room, role: 'user' });
const removeRoleResponse = await Subscriptions.removeRoleById(subscription._id, 'owner');
await syncRoomRolePriorityForUserAndRoom(userId, rid, subscription.roles?.filter((r) => r !== 'owner') || []);
if (removeRoleResponse.modifiedCount) {View on GitHub (pinned to b2c16d5842)
Solutions
- Inspect the target's subscription roles and only offer 'Remove owner' when they include 'owner'
- Refresh the member/roles list right before rendering actions
- Catch error-user-not-owner and treat it as 'already demoted'
Example fix
// before
await Meteor.callAsync('removeRoomOwner', rid, userId);
// after
const sub = Subscriptions.findOne({ rid, 'u._id': userId });
const roles = sub?.roles;
if (!Array.isArray(roles) || !roles.includes('owner')) return;
await Meteor.callAsync('removeRoomOwner', rid, userId); Defensive patterns
Strategy: type-guard
Validate before calling
const sub = Subscriptions.findOne({ rid, 'u._id': userId });
if (!Array.isArray(sub?.roles) || !sub.roles.includes('owner')) {
// not an owner: skip removal
} Type guard
const hasRoomRole = (roles: string[] | undefined | null, role: string): boolean =>
Array.isArray(roles) && roles.includes(role);
const isOwner = (sub?: { roles?: string[] }): boolean => hasRoomRole(sub?.roles, 'owner'); Try / catch
try {
await Meteor.callAsync('removeRoomOwner', rid, userId);
} catch (err) {
if (err instanceof Meteor.Error && err.error === 'error-user-not-owner') {
// already demoted: refresh and continue
}
} Prevention
- Render 'Remove owner' only when subscription roles include owner
- Refresh roles data before actions
- Note this check also fires when roles is missing entirely — stricter than the moderator variant
When it happens
Trigger: Calling removeRoomOwner on a plain member or a moderator; the owner role was already removed by another admin; subscription.roles missing entirely.
Common situations: Stale 'Owner' badge in a members modal after the role was stripped elsewhere; racing double-invocations of the action.
Related errors
- error-user-not-moderator
- error-invalid-user
- error-not-allowed
- error-user-not-in-room
- error-user-already-leader
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/7b25077230ae1fcb.
Report an issue: GitHub.