TechnitiumSoftware/DnsServer · error · DnsWebServiceException

User already exists: {newUsername}

Error message

User already exists: {newUsername}

What it means

Thrown as DnsWebServiceException from ChangeUsername when TryAdd of the new username fails because that name is already taken. The username is reverted to the old value before throwing, leaving state consistent. Returned over the API as HTTP 200 with status 'error'.

Source

Thrown at DnsServerCore/Auth/AuthManager.cs:986

        public bool HasDefaultCredentials()
        {
            User user = GetUser("admin");

            return (user is not null) && user.PasswordHash.Equals(user.GetPasswordHashFor("admin"), StringComparison.Ordinal);
        }

        public void ChangeUsername(User user, string newUsername)
        {
            if (user.Username.Equals(newUsername, StringComparison.OrdinalIgnoreCase))
                return;

            string oldUsername = user.Username;
            user.SetUsername(newUsername);

            if (!_users.TryAdd(user.Username, user))
            {
                user.SetUsername(oldUsername); //revert
                throw new DnsWebServiceException("User already exists: " + newUsername);
            }

            _users.TryRemove(oldUsername, out _);
        }

        public async Task<User> ChangePasswordAsync(string username, string password, string totp, IPAddress remoteAddress, string newPassword, int iterations)
        {
            User user = await AuthenticateUserAsync(username, password, totp, remoteAddress);

            user.ChangePassword(newPassword, iterations);

            return user;
        }

        public bool DeleteUser(string username)
        {
            if (_users.TryRemove(username.ToLowerInvariant(), out User deletedUser))
            {

View on GitHub (pinned to d0484b6c1e)

Solutions

  1. Choose a username that does not exist (check case-insensitively).
  2. Delete or rename the conflicting user first.
  3. Verify the target name via GetUser before attempting the rename.

Example fix

// before
_authManager.ChangeUsername(user, newUsername);
// after
if (GetUser(newUsername) is null)
    _authManager.ChangeUsername(user, newUsername);
Defensive patterns

Strategy: validation

Validate before calling

if (await GetUserAsync(newUsername) is not null)
    throw new InvalidOperationException($"Username '{newUsername}' is taken.");

Try / catch

try { await client.ChangeUsernameAsync(user, newUsername); }
catch (HttpApiClientException ex) when (ex.Message.StartsWith("User already exists"))
{
    PromptForDifferentUsername();
}

Prevention

When it happens

Trigger: POST /api/users/changeUsername (or equivalent) targeting a newUsername that already exists as a (lowercased) key; a no-op fast path exists only if the new name equals the current name case-insensitively.

Common situations: Renaming a user to a name that collides with another active or local user; case-only target that differs from current only by case still triggers the collision because keys are lowercased and equality is OrdinalIgnoreCase on the fast path.

Related errors


AI-assisted analysis of TechnitiumSoftware/DnsServer@d0484b6c1e (2026-08-13). Data as JSON: /api/errors/3e75a70b0b456f7f. Report an issue: GitHub.