RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-username

error-invalid-username

Error message

Invalid username

What it means

The account has NO usable local password (services.password.bcrypt empty or absent — typical of OAuth/LDAP/SSO-created users), so verification falls back to comparing the supplied value against SHA256(user.username). The throw means the account has no username at all, or the supplied digest does not equal sha256-hex of the username.

Source

Thrown at apps/meteor/server/meteor-methods/users/deleteUserOwnAccount.ts:52

	const user = await Users.findOneById(fromUserId);
	if (!user) {
		throw new Meteor.Error('error-invalid-user', 'Invalid user', {
			method: 'deleteUserOwnAccount',
		});
	}

	if (user.services?.password && trim(user.services.password.bcrypt)) {
		const result = await Accounts._checkPasswordAsync(user as Meteor.User, {
			digest: password.toLowerCase(),
			algorithm: 'sha-256',
		});
		if (result.error) {
			throw new Meteor.Error('error-invalid-password', 'Invalid password', {
				method: 'deleteUserOwnAccount',
			});
		}
	} else if (!user.username || SHA256(user.username) !== password.trim()) {
		throw new Meteor.Error('error-invalid-username', 'Invalid username', {
			method: 'deleteUserOwnAccount',
		});
	}

	await deleteUser(fromUserId, confirmRelinquish);

	// App IPostUserDeleted event hook
	await Apps.self?.triggerEvent(AppEvents.IPostUserDeleted, { user });

	return true;
};

Meteor.methods<ServerMethods>({
	async deleteUserOwnAccount(password, confirmRelinquish) {
		methodDeprecationLogger.method('deleteUserOwnAccount', '9.0.0', '/v1/users.deleteOwnAccount');
		check(password, String);

		const uid = Meteor.userId();

View on GitHub (pinned to b2c16d5842)

Solutions

  1. For passwordless accounts, send SHA256(user.username) hex as the password argument
  2. Ensure the account actually has a username; OAuth users without one cannot pass this check — set a username first
  3. Migrate to the REST endpoint /v1/users.deleteOwnAccount (the method is deprecated since 9.0.0) and follow its documented credential contract

Example fix

// before — SSO user sends their SSO password digest
await Meteor.callAsync('deleteUserOwnAccount', SHA256(ssoPassword).toString(), false);

// after — passwordless accounts verify against sha256(username)
const digest = SHA256(currentUser.username).toString();
await Meteor.callAsync('deleteUserOwnAccount', digest, false);
Defensive patterns

Strategy: try-catch

Validate before calling

// pick the credential the server will verify for this account type
const hasPassword = Boolean(user?.services?.password?.bcrypt);
const digest = SHA256(hasPassword ? password : user!.username!).toString();
await Meteor.callAsync('deleteUserOwnAccount', digest, false);

Type guard

const hasLocalPassword = (u: { services?: { password?: { bcrypt?: string } } } | null): boolean =>
	Boolean(u?.services?.password?.bcrypt && u.services.password.bcrypt.trim());

Try / catch

try {
	await Meteor.callAsync('deleteUserOwnAccount', digest, false);
} catch (err) {
	if ((err as { error?: string }).error === 'error-invalid-username') {
		// passwordless account: digest must equal sha256(username) — or account has no username
	}
}

Prevention

When it happens

Trigger: A passwordless (OAuth-only) account where the caller sends anything other than SHA256(username) as the password argument; or an SSO account that never got a username set (!user.username short-circuits the check).

Common situations: Google/CAS/GitHub SSO users trying the password-user delete flow with their SSO password; custom clients that always send a password digest; username-less bot or application accounts.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/3b7f8fc3304cd45c. Report an issue: GitHub.