RocketChat/Rocket.Chat · error · Meteor.Error

error-could-not-change-username

error-could-not-change-username

Error message

Could not change username

What it means

saveUserIdentity({_id, username}) returned false, meaning the low-level write path refused the change after all earlier checks in setUsernameWithValidation passed. In practice this is a race or second-order rejection: the user disappeared between the initial findOneById and saveUserIdentity's own fetch, validateName() inside saveUserIdentity rejected the name against Accounts_SystemBlockedUsernameList, or _setUsername's internal re-check of checkUsernameAvailability lost the name to a concurrent registration.

Source

Thrown at apps/meteor/server/lib/users/setUsername.ts:71

		return;
	}

	if (!validateUsername(username)) {
		throw new Meteor.Error(
			'username-invalid',
			`${_.escape(username)} is not a valid username, use only letters, numbers, dots, hyphens and underscores`,
		);
	}

	if (!(await checkUsernameAvailability(username))) {
		throw new Meteor.Error('error-field-unavailable', `<strong>${_.escape(username)}</strong> is already in use :(`, {
			method: 'setUsername',
			field: username,
		});
	}

	if (!(await saveUserIdentity({ _id: user._id, username }))) {
		throw new Meteor.Error('error-could-not-change-username', 'Could not change username', {
			method: 'setUsername',
		});
	}

	void notifyOnUserChange({ clientAction: 'updated', id: user._id, diff: { username } });
};

export const _setUsername = async function (
	userId: string,
	u: string,
	fullUser: IUser,
	updater?: Updater<IUser>,
	session?: ClientSession,
): Promise<unknown> {
	const username = u.trim();

	if (!userId || !username) {
		return false;

View on GitHub (pinned to b263243745)

Solutions

  1. Retry with a different username — the name was most likely claimed between the availability check and the write
  2. Re-fetch the user (it may have been deleted) and restart the flow if the account still exists
  3. For imports/bulk flows, serialize username assignment or pre-reserve names to avoid concurrent claims

Example fix

// before
await setUsernameWithValidation(userId, username); // error-could-not-change-username on races

// after
try {
  await setUsernameWithValidation(userId, username);
} catch (e) {
  if (e instanceof Meteor.Error && e.error === 'error-could-not-change-username') {
    username = `${username}-${Date.now() % 1000}`;
    await setUsernameWithValidation(userId, username); // retry with fallback
  }
}
Defensive patterns

Strategy: retry

Validate before calling

if (!(await checkUsernameAvailability(username))) {
  // name already lost: pick a suffixed alternative before the save
}

Try / catch

try {
  await setUsernameWithValidation(userId, username);
} catch (error) {
  if (error instanceof Meteor.Error && error.error === 'error-could-not-change-username') {
    const user = await Users.findOneById(userId, { projection: { _id: 1 } });
    if (user) {
      username = `${username}-${Date.now() % 1000}`;
      await setUsernameWithValidation(userId, username); // single retry
    }
  } else {
    throw error;
  }
}

Prevention

When it happens

Trigger: Two accounts claiming the same username at the same instant (the inner availability check returns false for the loser); the user document being deleted mid-flow; a username matching a system-blocked entry that only the inner validateName path checks.

Common situations: Concurrent signups or import rows picking the same name; long-running import scripts where users are deleted between steps; retries of partially failed renames.

Related errors


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