{"record":{"id":"dbcb2ffd5d7b1f09","repo":"RocketChat/Rocket.Chat","slug":"error-token-already-exists","errorCode":"error-token-already-exists","errorMessage":"A token with this name already exists","messagePattern":"A token with this name already exists","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts","lineNumber":37,"sourceCode":"\tuserId,\n}: {\n\ttokenName: string;\n\tuserId: string;\n\tbypassTwoFactor: boolean;\n}): Promise<string> => {\n\tif (!(await hasPermissionAsync(userId, 'create-personal-access-tokens'))) {\n\t\tthrow new Meteor.Error('not-authorized', 'Not Authorized', {\n\t\t\tmethod: 'personalAccessTokens:generateToken',\n\t\t});\n\t}\n\n\tconst token = Random.secret();\n\tconst tokenExist = await Users.findPersonalAccessTokenByTokenNameAndUserId({\n\t\tuserId,\n\t\ttokenName,\n\t});\n\tif (tokenExist) {\n\t\tthrow new Meteor.Error('error-token-already-exists', 'A token with this name already exists', {\n\t\t\tmethod: 'personalAccessTokens:generateToken',\n\t\t});\n\t}\n\n\tawait Users.addPersonalAccessTokenToUser({\n\t\tuserId,\n\t\tloginTokenObject: {\n\t\t\thashedToken: Accounts._hashLoginToken(token),\n\t\t\ttype: 'personalAccessToken',\n\t\t\tcreatedAt: new Date(),\n\t\t\tlastTokenPart: token.slice(-6),\n\t\t\tname: tokenName,\n\t\t\tbypassTwoFactor,\n\t\t},\n\t});\n\treturn token;\n};\n","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts#L19-L55","documentation":"Thrown by generatePersonalAccessTokenOfUser when Users.findPersonalAccessTokenByTokenNameAndUserId returns a record for that (userId, tokenName) pair. Personal access token names must be unique per user, so the duplicate check blocks the create. Token name uniqueness is enforced at the application layer before any insert.","triggerScenarios":"Calling generateToken with a tokenName the user already used; case sensitivity differences are not the cause (names are matched as-is); calling generate when a previous token with the same name exists (even if unused).","commonSituations":"User re-runs a setup script without removing prior tokens; UI does not refresh the token list so the user retries with the same name; automation picks a generic name like 'ci-token' that already exists.","solutions":["Choose a different tokenName for the new token.","If you want to refresh an existing token, call regenerateToken instead of generateToken.","Remove the existing token first with removeToken, then generate.","List existing tokens before generating to surface collisions to the user."],"exampleFix":"// before\nawait generatePersonalAccessTokenOfUser({ userId, tokenName: 'ci-token', bypassTwoFactor: false }); // already exists\n\n// after\nawait regeneratePersonalAccessTokenOfUser('ci-token', userId);","handlingStrategy":"validation","validationCode":"async function tokenNameIsFree(userId: string, tokenName: string): Promise<boolean> {\n  const existing = await Users.findPersonalAccessTokenByTokenNameAndUserId({ userId, tokenName });\n  return !existing;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await generatePersonalAccessTokenOfUser({ userId, tokenName, bypassTwoFactor });\n} catch (e) {\n  if (e.error === 'error-token-already-exists') {\n    // prompt: regenerate existing, or pick a new name\n  } else throw e;\n}","preventionTips":["List existing token names before letting the user pick one.","Default to regenerateToken when the name already exists.","Use unique, descriptive token names in automation to avoid collisions."],"tags":["personal-access-tokens","duplicate","validation"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}