{"record":{"id":"4cd06381d9ef462f","repo":"RocketChat/Rocket.Chat","slug":"error-token-does-not-exists-4cd063","errorCode":"error-token-does-not-exists","errorMessage":"Token does not exist","messagePattern":"Token does not exist","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts","lineNumber":25,"sourceCode":"declare module '@rocket.chat/ddp-client' {\n\t// eslint-disable-next-line @typescript-eslint/naming-convention\n\tinterface ServerMethods {\n\t\t'personalAccessTokens:removeToken'(params: { tokenName: string }): Promise<void>;\n\t}\n}\n\nexport const removePersonalAccessTokenOfUser = async (tokenName: string, userId: string): Promise<void> => {\n\tif (!(await hasPermissionAsync(userId, 'create-personal-access-tokens'))) {\n\t\tthrow new Meteor.Error('not-authorized', 'Not Authorized', {\n\t\t\tmethod: 'personalAccessTokens:removeToken',\n\t\t});\n\t}\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-does-not-exists', 'Token does not exist', {\n\t\t\tmethod: 'personalAccessTokens:removeToken',\n\t\t});\n\t}\n\tawait Users.removePersonalAccessTokenOfUser({\n\t\tuserId,\n\t\tloginTokenObject: {\n\t\t\ttype: 'personalAccessToken',\n\t\t\tname: tokenName,\n\t\t},\n\t});\n};\n\nMeteor.methods<ServerMethods>({\n\t'personalAccessTokens:removeToken': twoFactorRequired(async function ({ tokenName }: { tokenName: string }) {\n\t\tconst uid = Meteor.userId();\n\t\tif (!uid) {\n\t\t\tthrow new Meteor.Error('not-authorized', 'Not Authorized', {\n\t\t\t\tmethod: 'personalAccessTokens:removeToken',","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts#L7-L43","documentation":"removePersonalAccessTokenOfUser first resolves the token by name via Users.findPersonalAccessTokenByTokenNameAndUserId({ userId, tokenName }); if no token with that name exists for the user, it throws error-token-does-not-exists before modifying loginTokens.","triggerScenarios":"Calling personalAccessTokens:removeToken for an already-removed token (double delete), a name that never existed, or a case-mismatched name.","commonSituations":"Idempotent-delete races in UIs (double-click), cleanup scripts re-running, tokens already rotated/removed elsewhere.","solutions":["Treat it as success if the goal is 'token must not exist' — it is already gone.","Otherwise verify the exact token name against the user's token list and retry.","Guard re-runnable scripts so a missing token is not an error."],"exampleFix":"// before\nawait Meteor.callAsync('personalAccessTokens:removeToken', { tokenName });\n\n// after — idempotent delete\ntry {\n\tawait Meteor.callAsync('personalAccessTokens:removeToken', { tokenName });\n} catch (e: any) {\n\tif (e?.error !== 'error-token-does-not-exists') throw e; // already removed: fine\n}","handlingStrategy":"fallback","validationCode":"const token = await Users.findPersonalAccessTokenByTokenNameAndUserId({ userId, tokenName });\nif (!token) return; // goal already achieved: token absent","typeGuard":null,"tryCatchPattern":"try {\n\tawait Meteor.callAsync('personalAccessTokens:removeToken', { tokenName });\n} catch (e: any) {\n\tif (e?.error !== 'error-token-does-not-exists') throw e; // already gone — treat as success\n}","preventionTips":["Make deletion flows idempotent: a missing token is the desired end state","Debounce double-clicks on remove buttons","In cleanup scripts, check existence first or swallow the not-exists error explicitly"],"tags":["personal-access-tokens","validation","idempotency"],"backgroundTag":"entity-not-found","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"}