SonarSource/sonarqube · error · NotFoundException

User '%s' doesn't exist

Error message

User '%s' doesn't exist

What it means

UpdateIdentityProviderAction (api/users/update_identity_provider) throws this NotFoundException when the login supplied does not belong to an existing, active user. The action moves a user to a new identity provider (e.g. from LDAP realm to SAML/GitHub) and refuses to act on absent or deactivated accounts. Note: logins originating from the LDAP security realm carry a special prefix and cannot be relinked.

Source

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

    checkArgument(isAllowedProvider, "Value of parameter 'newExternalProvider' (%s) must be one of: [%s] or [%s]", newExternalProvider,
      String.join(", ", allowedIdentityProviders), String.join(", ", "LDAP", "LDAP_{serverKey}"));
  }

  private List<String> getAvailableIdentityProviders() {
    return identityProviderRepository.getAllEnabledAndSorted()
      .stream()
      .map(IdentityProvider::getKey)
      .toList();
  }

  private static boolean isLdapIdentityProvider(String identityProviderKey) {
    return identityProviderKey.startsWith(LDAP_SECURITY_REALM);
  }

  private UserDto getUser(DbSession dbSession, String login) {
    UserDto user = dbClient.userDao().selectByLogin(dbSession, login);
    if (user == null || !user.isActive()) {
      throw new NotFoundException(format("User '%s' doesn't exist", login));
    }
    return user;
  }

  private static UpdateUser toUpdateUser(UpdateIdentityProviderRequest request, UserDto user) {
    return new UpdateUser()
      .setExternalIdentityProvider(request.newExternalProvider)
      .setExternalIdentityProviderLogin(Optional.ofNullable(request.newExternalIdentity).orElse(user.getExternalLogin())
      );
  }

  private static UpdateIdentityProviderRequest toWsRequest(Request request) {
    return UpdateIdentityProviderRequest.builder()
      .setLogin(request.mandatoryParam(PARAM_LOGIN))
      .setNewExternalProvider(replaceDeprecatedSonarqubeIdentityProviderByLdapForSonar17508(request.mandatoryParam(PARAM_NEW_EXTERNAL_PROVIDER)))
      .setNewExternalIdentity(request.param(PARAM_NEW_EXTERNAL_IDENTITY))
      .build();
  }

View on GitHub (pinned to 184c821202)

Solutions

  1. Verify the login exists and is active (GET api/users/search) before running the identity-provider migration.
  2. Reactivate the user (active=true) if it was disabled, then retry.
  3. For users missing in the target IdP, provision them by having them sign in once with the new provider first, or create them via api/users/create.
  4. Skip users whose identity is still managed by the LDAP security realm — they cannot be relinked via this endpoint.

Example fix

// before: bulk migration blindly iterating a stale user list
for u in $USERS; do curl -su "$ADMIN:" -X POST "https://sonar/api/users/update_identity_provider?login=$u&newIdentityProvider=saml"; done
// after: filter to active users first
curl -su "$ADMIN:" 'https://sonar/api/users/search?active=true' | jq -r '.users[].login'
for u in $ACTIVE_USERS; do curl -su "$ADMIN:" -X POST "https://sonar/api/users/update_identity_provider?login=$u&newIdentityProvider=saml"; done
Defensive patterns

Strategy: validation

Validate before calling

const {users} = await fetch(`${base}/api/users/search?q=${encodeURIComponent(login)}`, {headers: auth}).then(r => r.json());
const user = users.find(u => u.login === login && u.active);
if (!user) throw new Error(`cannot relink ${login}: missing or inactive`);
if (user.externalProvider === 'ldap' || user.externalProvider === 'sonarqube-ldap') throw new Error('LDAP realm identity cannot be relinked');

Type guard

function canRelinkIdentity(u) {
  return u != null && u.active === true && !/^ldap/i.test(String(u.externalProvider ?? ''));
}

Try / catch

try {
  await updateIdentityProvider(login, newProvider);
} catch (e) {
  if (e.status === 404 && /doesn't exist/.test(e.message)) {
    migrations.push({login, reason: 'missing-or-inactive'}); // handle out of band
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: POST api/users/update_identity_provider with a login that has no user row or has active=false; also attempting to relink a user whose identityProviderKey starts with the LDAP security realm prefix.

Common situations: Migrating authentication from LDAP to SAML/GitHub where some users never signed in and were never provisioned; user deactivated during offboarding but migration script still lists them; login mismatch after directory rename.

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/267d9c801993d582. Report an issue: GitHub.