{"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/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts#L19-L55","documentation":"Personal access token names are unique per user. generatePersonalAccessTokenOfUser first looks up Users.findPersonalAccessTokenByTokenNameAndUserId({ userId, tokenName }); if a token with that name already exists for the user it aborts with error-token-already-exists before creating a new secret.","triggerScenarios":"Calling personalAccessTokens:generateToken with a tokenName the same user already used — double-submitted create form, retry after a network failure, or simply forgetting an earlier token.","commonSituations":"CI scripts hard-coding a token name on every run, UI double-click on 'Add', rotating tokens by re-creating instead of regenerating.","solutions":["Choose a different, unique tokenName for the new token.","If you want a new secret under the existing name, call personalAccessTokens:regenerateToken instead.","Or remove the old token first with personalAccessTokens:removeToken, then generate."],"exampleFix":"// before\nawait Meteor.callAsync('personalAccessTokens:generateToken', { tokenName: 'ci-deploy', bypassTwoFactor: false });\n\n// after — name already used: rotate the secret instead\nawait Meteor.callAsync('personalAccessTokens:regenerateToken', { tokenName: 'ci-deploy' });","handlingStrategy":"validation","validationCode":"const existing = await Users.findPersonalAccessTokenByTokenNameAndUserId({ userId, tokenName });\nif (existing) {\n\t// rotate instead: call personalAccessTokens:regenerateToken\n}","typeGuard":null,"tryCatchPattern":"try {\n\tawait Meteor.callAsync('personalAccessTokens:generateToken', { tokenName, bypassTwoFactor: false });\n} catch (e: any) {\n\tif (e?.error === 'error-token-already-exists') {\n\t\tawait Meteor.callAsync('personalAccessTokens:regenerateToken', { tokenName }); // desired behavior: rotate\n\t} else throw e;\n}","preventionTips":["Fetch the user's existing token names before showing the create form","Use regenerateToken for secret rotation; reserve generateToken for first creation","Guard against double-submit in the UI"],"tags":["personal-access-tokens","duplicate","validation"],"backgroundTag":"duplicate-resource","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}