RocketChat/Rocket.Chat · error · MeteorError
error-extension-not-available
error-extension-not-available
Error message
Extension is already assigned to another user
What it means
Thrown by canEditExtension() (validateUserEditing.ts:21) when the VoIP/FreeSWITCH integration is enabled (VoIP_TeamCollab_SIP_Integration_Enabled), a new freeSwitchExtension value is being set, and Users.findOneByFreeSwitchExtension finds ANOTHER user already owning that extension number. Extensions are a one-user-per-number resource. Note the inverse case: when the VoIP setting is off, canEditExtension returns false and the caller instead gets the separate 'Edit user voice call extension is not allowed' error.
Source
Thrown at apps/meteor/server/lib/users/saveUser/validateUserEditing.ts:21
import type { IUser } from '@rocket.chat/core-typings';
import { Users } from '@rocket.chat/models';
import type { UpdateUserData } from './saveUser';
import { settings } from '../../../settings';
import { hasPermissionAsync } from '../../authorization/hasPermission';
const isEditingUserRoles = (previousRoles: IUser['roles'], newRoles?: IUser['roles']) =>
newRoles !== undefined &&
(newRoles.some((item) => !previousRoles.includes(item)) || previousRoles.some((item) => !newRoles.includes(item)));
const isEditingField = (previousValue?: string, newValue?: string) => typeof newValue !== 'undefined' && newValue !== previousValue;
export const canEditExtension = async (newExtension?: string) => {
if (!settings.get('VoIP_TeamCollab_SIP_Integration_Enabled')) {
return false;
}
if (newExtension && (await Users.findOneByFreeSwitchExtension(newExtension, { projection: { _id: 1 } }))) {
throw new MeteorError('error-extension-not-available', 'Extension is already assigned to another user');
}
return true;
};
/**
* Validate permissions to edit user fields
*
* @param {string} userId
* @param {{ _id: string, roles?: string[], username?: string, name?: string, statusText?: string, email?: string, password?: string}} userData
*/
export async function validateUserEditing(userId: IUser['_id'], userData: UpdateUserData): Promise<void> {
const editingMyself = userData._id && userId === userData._id;
const canEditOtherUserInfo = await hasPermissionAsync(userId, 'edit-other-user-info');
const canEditOtherUserPassword = await hasPermissionAsync(userId, 'edit-other-user-password');
const user = await Users.findOneById(userData._id);
View on GitHub (pinned to b2c16d5842)
Solutions
- Find the current owner and release the number first: update that user's freeSwitchExtension to another value or clear it, then retry.
- Assign a free extension: query users and exclude all freeSwitchExtension values already taken before choosing.
- If the number genuinely belongs to the new user, deactivate or renumber the old holder as part of the same change.
Example fix
// before
await POST '/api/v1/users.update', { userId: newId, data: { freeSwitchExtension: '1001' } }); // throws: in use
// after: free it from the previous owner first
await POST '/api/v1/users.update', { userId: previousOwnerId, data: { freeSwitchExtension: '' } });
await POST '/api/v1/users.update', { userId: newId, data: { freeSwitchExtension: '1001' } }); Defensive patterns
Strategy: try-catch
Validate before calling
// best-effort pre-check: extensions are not indexed in a public filter, so detect collisions via admin search
const taken = new Set((await GET '/api/v1/users.list?count=0&fields={"freeSwitchExtension":1}') /* page through */).users.map((u) => u.freeSwitchExtension));
if (taken.has(ext)) throw new Error('extension in use'); Type guard
null
Try / catch
catch (e) {
if (e.error === 'error-extension-not-available') {
// locate owner via admin users list, release or pick another extension, then retry once
}
} Prevention
- Maintain the extension directory outside Rocket.Chat as the source of truth.
- Assign extensions from a pool that excludes used numbers.
- Recheck on every VoIP renumbering project.
When it happens
Trigger: users.update assigning freeSwitchExtension '1001' when a different user already has extension '1001' in the FreeSWITCH admin panel; reassigning an extension that a departed employee still holds; importing PBX users whose extensions overlap existing accounts.
Common situations: Admin console user form with the Voice Call extension field; VoIP setup where the PBX was renumbered but Rocket.Chat users kept old extensions; two admins editing simultaneously, the second one colliding.
Related errors
- error-field-unavailable
- error-invalid-user
- error-user-not-found
- error-could-not-save-identity
- error-action-not-allowed
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/31d6dd3d6bee4e41.
Report an issue: GitHub.