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
- For passwordless accounts, send SHA256(user.username) hex as the password argument
- Ensure the account actually has a username; OAuth users without one cannot pass this check — set a username first
- 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
- Branch the delete flow on whether the account has services.password.bcrypt
- For OAuth/SSO accounts, send sha256(username) — not the SSO password
- Ensure OAuth provisioned accounts always get a username at creation
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.