{"record":{"id":"d0227ca98797b812","repo":"SonarSource/sonarqube","slug":"a-rule-with-the-key-s-already-exists","errorCode":null,"errorMessage":"A rule with the key '%s' already exists","messagePattern":"A rule with the key '(.+?)' already exists","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-common/src/main/java/org/sonar/server/common/rule/RuleCreator.java","lineNumber":292,"sourceCode":"      } else {\n        ruleDto.setStatus(RuleStatus.READY)\n          .setName(newRule.name())\n          .replaceRuleDescriptionSectionDtos(Set.of(\n            createDefaultRuleDescriptionSection(uuidFactory.create(), newRule.markdownDescription())))\n          .setUpdatedAt(system2.now());\n\n        ruleDto.replaceAllDefaultImpacts(Set.of());\n        setCleanCodeAttributeAndImpacts(newRule, ruleDto, templateRule);\n\n        dbClient.ruleDao().update(dbSession, ruleDto);\n        for (RuleParamDto ruleParamDto : dbClient.ruleDao().selectRuleParamsByRuleKey(dbSession, ruleDto.getKey())) {\n          String newValue = Strings.emptyToNull(newRule.parameter(ruleParamDto.getName()));\n          ruleParamDto.setDefaultValue(newValue);\n          dbClient.ruleDao().updateRuleParam(dbSession, ruleDto, ruleParamDto);\n        }\n      }\n    } else {\n      throw new IllegalArgumentException(format(\"A rule with the key '%s' already exists\", ruleDto.getKey().rule()));\n    }\n    return ruleDto;\n  }\n\n}\n","sourceCodeStart":274,"sourceCodeEnd":298,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-common/src/main/java/org/sonar/server/common/rule/RuleCreator.java#L274-L298","documentation":"RuleCreator.updateExistingRule throws this IllegalArgumentException when an update operation reaches the 'create' branch of the rule creation logic, meaning a rule with the given key already exists in the database. It guards against inserting a duplicate rule key when the caller expected to create a new rule.","triggerScenarios":"Calling the rules/create WS (or RuleCreator.create) with a rule key that already exists in the rule table, so the DAO lookup finds an existing ruleDto and control falls into the else branch that throws.","commonSituations":"Re-running an automation script that provisions custom rules without idempotency checks; importing a rules profile that already defines the rule; two CI jobs creating the same custom rule concurrently.","solutions":["Check rule existence first via api/rules/show?key=... or RuleDao.selectRuleByKey and use the update endpoint (api/rules/update) instead of create.","Make provisioning scripts idempotent: create if absent, update if present.","Use a different, unique rule key if you truly intend a new rule.","Serialize rule provisioning so concurrent jobs cannot race on the same key."],"exampleFix":"// before\nruleUpdater.create(createRequest); // fails when key exists\n// after\nif (ruleDao.selectRuleByKey(dbSession, key).isPresent()) {\n  ruleUpdater.update(updateRequest);\n} else {\n  ruleUpdater.create(createRequest);\n}","handlingStrategy":"validation","validationCode":"RuleDto existing = dbClient.ruleDao().selectRuleByKey(dbSession, key).orElse(null);\nif (existing != null) { /* update instead of create */ }","typeGuard":null,"tryCatchPattern":"try { creator.create(req); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"already exists\")) { updater.update(toUpdate(req)); } else { throw e; } }","preventionTips":["Look up the rule by key before creating","Make provisioning pipelines idempotent","Use api/rules/update for existing keys","Lock rule-creation jobs to a single runner"],"tags":["rules","duplicate-key","illegal-argument","java"],"backgroundTag":"file-already-exists","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"}