{"record":{"id":"63c83359c5c8b49a","repo":"SonarSource/sonarqube","slug":"user-s-is-not-deactivated","errorCode":null,"errorMessage":"User '%s' is not deactivated","messagePattern":"User '(.+?)' is not deactivated","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/AnonymizeAction.java","lineNumber":73,"sourceCode":"      .setDeprecatedSince(\"10.4\")\n      .setChangelog(new Change(\"10.4\", \"Deprecated. Use DELETE api/v2/users-management/users/{id}?anonymize=true instead\"));\n\n    action.createParam(PARAM_LOGIN)\n      .setDescription(\"User login\")\n      .setRequired(true)\n      .setExampleValue(\"myuser\");\n  }\n\n  @Override\n  public void handle(Request request, Response response) throws Exception {\n    userSession.checkLoggedIn().checkIsSystemAdministrator();\n    String login = request.mandatoryParam(PARAM_LOGIN);\n\n    try (DbSession dbSession = dbClient.openSession(false)) {\n      UserDto user = dbClient.userDao().selectByLogin(dbSession, login);\n      checkFound(user, \"User '%s' doesn't exist\", login);\n      if (user.isActive()) {\n        throw new IllegalArgumentException(String.format(\"User '%s' is not deactivated\", login));\n      }\n\n      userAnonymizer.anonymize(dbSession, user);\n      dbClient.userDao().update(dbSession, user);\n      dbSession.commit();\n    }\n\n    response.noContent();\n  }\n\n}\n","sourceCodeStart":55,"sourceCodeEnd":85,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/user/ws/AnonymizeAction.java#L55-L85","documentation":"api/users/anonymize only works on deactivated users: AnonymizeAction.handle loads the user by login and throws IllegalArgumentException if user.isActive(), because anonymizing an active user would break their sessions/assignments expectations. The user record is untouched when this fires.","triggerScenarios":"POST api/users/anonymize?login=x where the user exists but is still active (has not been deactivated via api/users/deactivate).","commonSituations":"Admin scripts skipping the deactivate step; SCIM/automation assuming anonymize implies deactivation; retrying anonymize for a user that was re-activated.","solutions":["Call POST api/users/deactivate?login=x first, then anonymize","Check the user's active=false status (api/users/search) before anonymizing","If the user was re-activated by mistake, deactivate again before retrying"],"exampleFix":"// before\nPOST /api/users/anonymize?login=jdoe\n// after\nPOST /api/users/deactivate?login=jdoe\nPOST /api/users/anonymize?login=jdoe","handlingStrategy":"validation","validationCode":"const user = await getUser(login);\nif (user.active) throw new Error(`Deactivate user '${login}' before anonymizing`);","typeGuard":"function canAnonymize(user) { return user != null && user.active === false; }","tryCatchPattern":"try { await anonymizeUser(login); } catch (e) { if (/is not deactivated/.test(e.message)) { await deactivateUser(login); return anonymizeUser(login); } throw e; }","preventionTips":["Always pair anonymize with a preceding deactivate call","Check user.active via api/users/search before anonymizing","Guard re-activation workflows so anonymized users are never re-enabled"],"tags":["users","api","state","prerequisites"],"backgroundTag":"invalid-state-transition","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}