SonarSource/sonarqube · error · NotFoundException

User with login '%s' has not been found

Error message

User with login '%s' has not been found

What it means

SonarQube's user web API (ChangePasswordAction) throws this NotFoundException when the login passed to the change-password endpoint does not resolve to an existing AND active user. The DAO selectByLogin returns null for missing users, and deactived users are treated the same as missing ones so their credentials cannot be manipulated. It is the API's way of saying the target account is absent or disabled.

Source

Thrown at server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/ChangePasswordAction.java:206

  private void checkPreviousPassword(DbSession dbSession, UserDto user, String password) throws PasswordException {
    try {
      localAuthentication.authenticate(dbSession, user, password, AuthenticationEvent.Method.BASIC);
    } catch (AuthenticationException ex) {
      throw new PasswordException(OLD_PASSWORD_INCORRECT, "Incorrect password");
    }
  }

  private static void checkNewPasswordSameAsOld(String newPassword, String previousPassword) throws PasswordException {
    if (previousPassword.equals(newPassword)) {
      throw new PasswordException(NEW_PASSWORD_SAME_AS_OLD, "Password must be different from old password");
    }
  }

  private UserDto getUserOrThrow(DbSession dbSession, String login) {
    UserDto user = dbClient.userDao().selectByLogin(dbSession, login);
    if (user == null || !user.isActive()) {
      throw new NotFoundException(format("User with login '%s' has not been found", login));
    }
    return user;
  }

  private void deleteTokensAndRefreshSession(HttpRequest request, HttpResponse response, DbSession dbSession, UserDto user) {
    dbClient.sessionTokensDao().deleteByUser(dbSession, user);
    refreshJwtToken(request, response, user);
  }

  private void refreshJwtToken(HttpRequest request, HttpResponse response, UserDto user) {
    jwtHttpHandler.removeToken(request, response);
    jwtHttpHandler.generateToken(user, request, response);
  }

  private void updatePassword(DbSession dbSession, UserDto user, String newPassword) {
    UpdateUser updateUser = new UpdateUser().setPassword(newPassword);
    userUpdater.updateAndCommit(dbSession, user, updateUser, u -> {
    });

View on GitHub (pinned to 184c821202)

Solutions

  1. Verify the login exists and is active: GET api/users/search?q=<login> as an administrator, and confirm the 'login' field (not name or email).
  2. If the user is deactivated, reactivate first via POST api/users/update with active=true (requires Administer System).
  3. Create the user if missing: POST api/users/create with the correct login, then retry the password change.
  4. If login identity comes from an external auth (LDAP/SAML/GitHub), the login may be provisioned on first sign-in — have the user sign in once, then retry.
  5. Check for whitespace/case issues in the login parameter; logins are stored exactly as created.

Example fix

// before: querying by email instead of login
curl -u admin:token -X POST 'https://sonar/api/users/change_password?login=jane@corp.com&password=...'
// after: use the actual login, after verifying it exists
curl -u admin:token 'https://sonar/api/users/search?q=jdoe'   # find exact login
curl -u admin:token -X POST 'https://sonar/api/users/change_password?login=jdoe&password=...'
Defensive patterns

Strategy: validation

Validate before calling

const exists = await fetch(`${base}/api/users/search?q=${encodeURIComponent(login)}`, {headers: auth});
const {users} = await exists.json();
const user = users.find(u => u.login === login && u.active);
if (!user) throw new Error(`login ${login} missing or inactive; aborting change_password`);

Type guard

function isActiveUser(u) {
  return u != null && typeof u.login === 'string' && u.active === true;
}

Try / catch

try {
  await changePassword(login, newPassword);
} catch (e) {
  if (e.status === 404 && /has not been found/.test(e.message)) {
    // user missing or deactivated: look it up / reactivate before retry
  } else throw e;
}

Prevention

When it happens

Trigger: POST api/users/change_password with a 'login' parameter that matches no user row, or matches a user whose active=false.

Common situations: Typo in login; user was deactivated/deleted by an admin; referring to a user by name or email instead of login; user was removed after token/credential cleanup scripts; SAML/LDAP-provisioned account not yet created locally.

Understand the failure class

Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/c7d44b2cd41207a9. Report an issue: GitHub.