RocketChat/Rocket.Chat · error · Meteor.Error

error-token-already-exists

error-token-already-exists

Error message

A token with this name already exists

What it means

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.

Source

Thrown at apps/meteor/imports/personal-access-tokens/server/api/methods/generateToken.ts:37

	userId,
}: {
	tokenName: string;
	userId: string;
	bypassTwoFactor: boolean;
}): Promise<string> => {
	if (!(await hasPermissionAsync(userId, 'create-personal-access-tokens'))) {
		throw new Meteor.Error('not-authorized', 'Not Authorized', {
			method: 'personalAccessTokens:generateToken',
		});
	}

	const token = Random.secret();
	const tokenExist = await Users.findPersonalAccessTokenByTokenNameAndUserId({
		userId,
		tokenName,
	});
	if (tokenExist) {
		throw new Meteor.Error('error-token-already-exists', 'A token with this name already exists', {
			method: 'personalAccessTokens:generateToken',
		});
	}

	await Users.addPersonalAccessTokenToUser({
		userId,
		loginTokenObject: {
			hashedToken: Accounts._hashLoginToken(token),
			type: 'personalAccessToken',
			createdAt: new Date(),
			lastTokenPart: token.slice(-6),
			name: tokenName,
			bypassTwoFactor,
		},
	});
	return token;
};

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Choose a different tokenName for the new token.
  2. If you want to refresh an existing token, call regenerateToken instead of generateToken.
  3. Remove the existing token first with removeToken, then generate.
  4. List existing tokens before generating to surface collisions to the user.

Example fix

// before
await generatePersonalAccessTokenOfUser({ userId, tokenName: 'ci-token', bypassTwoFactor: false }); // already exists

// after
await regeneratePersonalAccessTokenOfUser('ci-token', userId);
Defensive patterns

Strategy: validation

Validate before calling

async function tokenNameIsFree(userId: string, tokenName: string): Promise<boolean> {
  const existing = await Users.findPersonalAccessTokenByTokenNameAndUserId({ userId, tokenName });
  return !existing;
}

Try / catch

try {
  await generatePersonalAccessTokenOfUser({ userId, tokenName, bypassTwoFactor });
} catch (e) {
  if (e.error === 'error-token-already-exists') {
    // prompt: regenerate existing, or pick a new name
  } else throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/dbcb2ffd5d7b1f09. Report an issue: GitHub.