RocketChat/Rocket.Chat · error · Meteor.Error
error-token-does-not-exists
error-token-does-not-exists
Error message
Token does not exist
What it means
Thrown by removePersonalAccessTokenOfUser when Users.findPersonalAccessTokenByTokenNameAndUserId returns null — there is no token with that name to remove. The deletion is a no-op only at the DB level; the application treats a missing token as an error so callers know the name was wrong.
Source
Thrown at apps/meteor/imports/personal-access-tokens/server/api/methods/removeToken.ts:25
declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
'personalAccessTokens:removeToken'(params: { tokenName: string }): Promise<void>;
}
}
export const removePersonalAccessTokenOfUser = async (tokenName: string, userId: string): Promise<void> => {
if (!(await hasPermissionAsync(userId, 'create-personal-access-tokens'))) {
throw new Meteor.Error('not-authorized', 'Not Authorized', {
method: 'personalAccessTokens:removeToken',
});
}
const tokenExist = await Users.findPersonalAccessTokenByTokenNameAndUserId({
userId,
tokenName,
});
if (!tokenExist) {
throw new Meteor.Error('error-token-does-not-exists', 'Token does not exist', {
method: 'personalAccessTokens:removeToken',
});
}
await Users.removePersonalAccessTokenOfUser({
userId,
loginTokenObject: {
type: 'personalAccessToken',
name: tokenName,
},
});
};
Meteor.methods<ServerMethods>({
'personalAccessTokens:removeToken': twoFactorRequired(async function ({ tokenName }: { tokenName: string }) {
const uid = Meteor.userId();
if (!uid) {
throw new Meteor.Error('not-authorized', 'Not Authorized', {
method: 'personalAccessTokens:removeToken',View on GitHub (pinned to f9d3ec372b)
Solutions
- Treat the error as success if the goal is simply 'token must not exist' (idempotent remove).
- Refresh the token list before allowing a delete action.
- Verify the exact tokenName spelling and case.
- On the UI, hide the deleted token row immediately to prevent double submits.
Example fix
// before
await removePersonalAccessTokenOfUser(tokenName, userId); // throws if already gone
// after
try {
await removePersonalAccessTokenOfUser(tokenName, userId);
} catch (e) {
if (e.error !== 'error-token-does-not-exists') throw e;
// already removed — treat as success
} Defensive patterns
Strategy: try-catch
Validate before calling
async function tokenExists(userId: string, tokenName: string): Promise<boolean> {
const t = await Users.findPersonalAccessTokenByTokenNameAndUserId({ userId, tokenName });
return Boolean(t);
} Try / catch
try {
await removePersonalAccessTokenOfUser(tokenName, userId);
} catch (e) {
if (e.error === 'error-token-does-not-exists') {
// treat as success — token is already gone (idempotent remove)
} else throw e;
} Prevention
- Make remove actions idempotent by swallowing this specific error when appropriate.
- Hide the deleted row immediately in the UI to prevent double submits.
- Verify tokenName spelling/case before deleting.
When it happens
Trigger: Calling removeToken with a tokenName that was already deleted, never existed, or is misspelled; double-submit of a remove action.
Common situations: User clicks delete twice; another session already removed it; stale UI list after deletion; typo in the name.
Related errors
- error-token-does-not-exists
- Room id not found
- A video conference must exist to update.
- error-canned-response-not-found
- error-token-already-exists
AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12).
Data as JSON: /api/errors/4cd06381d9ef462f.
Report an issue: GitHub.