{"record":{"id":"d865351c8882eab3","repo":"RocketChat/Rocket.Chat","slug":"error-could-not-change-username","errorCode":"error-could-not-change-username","errorMessage":"Could not change username","messagePattern":"Could not change username","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/users/setUsername.ts","lineNumber":71,"sourceCode":"\t\treturn;\n\t}\n\n\tif (!validateUsername(username)) {\n\t\tthrow new Meteor.Error(\n\t\t\t'username-invalid',\n\t\t\t`${_.escape(username)} is not a valid username, use only letters, numbers, dots, hyphens and underscores`,\n\t\t);\n\t}\n\n\tif (!(await checkUsernameAvailability(username))) {\n\t\tthrow new Meteor.Error('error-field-unavailable', `<strong>${_.escape(username)}</strong> is already in use :(`, {\n\t\t\tmethod: 'setUsername',\n\t\t\tfield: username,\n\t\t});\n\t}\n\n\tif (!(await saveUserIdentity({ _id: user._id, username }))) {\n\t\tthrow new Meteor.Error('error-could-not-change-username', 'Could not change username', {\n\t\t\tmethod: 'setUsername',\n\t\t});\n\t}\n\n\tvoid notifyOnUserChange({ clientAction: 'updated', id: user._id, diff: { username } });\n};\n\nexport const _setUsername = async function (\n\tuserId: string,\n\tu: string,\n\tfullUser: IUser,\n\tupdater?: Updater<IUser>,\n\tsession?: ClientSession,\n): Promise<unknown> {\n\tconst username = u.trim();\n\n\tif (!userId || !username) {\n\t\treturn false;","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b263243745917337314259cf987c0e989cf0cdc9/apps/meteor/server/lib/users/setUsername.ts#L53-L89","documentation":"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.","triggerScenarios":"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.","commonSituations":"Concurrent signups or import rows picking the same name; long-running import scripts where users are deleted between steps; retries of partially failed renames.","solutions":["Retry with a different username — the name was most likely claimed between the availability check and the write","Re-fetch the user (it may have been deleted) and restart the flow if the account still exists","For imports/bulk flows, serialize username assignment or pre-reserve names to avoid concurrent claims"],"exampleFix":"// before\nawait setUsernameWithValidation(userId, username); // error-could-not-change-username on races\n\n// after\ntry {\n  await setUsernameWithValidation(userId, username);\n} catch (e) {\n  if (e instanceof Meteor.Error && e.error === 'error-could-not-change-username') {\n    username = `${username}-${Date.now() % 1000}`;\n    await setUsernameWithValidation(userId, username); // retry with fallback\n  }\n}","handlingStrategy":"retry","validationCode":"if (!(await checkUsernameAvailability(username))) {\n  // name already lost: pick a suffixed alternative before the save\n}","typeGuard":null,"tryCatchPattern":"try {\n  await setUsernameWithValidation(userId, username);\n} catch (error) {\n  if (error instanceof Meteor.Error && error.error === 'error-could-not-change-username') {\n    const user = await Users.findOneById(userId, { projection: { _id: 1 } });\n    if (user) {\n      username = `${username}-${Date.now() % 1000}`;\n      await setUsernameWithValidation(userId, username); // single retry\n    }\n  } else {\n    throw error;\n  }\n}","preventionTips":["Treat this error as 'lost a race' first: re-check availability before deeper debugging","Bulk imports: assign usernames sequentially or with unique suffixes","Verify the user document still exists before retrying"],"tags":["username","race-condition","save","meteor"],"backgroundTag":"concurrent-write-conflict","analyzedSha":"b263243745917337314259cf987c0e989cf0cdc9","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}