SonarSource/sonarqube · error · IllegalArgumentException

Specified RuleKey '%s' is not equal to the one already regis

Error message

Specified RuleKey '%s' is not equal to the one already registered in repository for ref %s: '%s'

What it means

RuleRepositoryImpl.register() keeps a map from a dump ref to the rule registered under it. If a ref is registered twice with a different RuleKey than the one already stored, an IllegalArgumentException is thrown because two different rules are being mapped to the same reference, which would corrupt the dump's rule index. Registering the same identical RuleKey under a ref is idempotent and returns the existing Rule.

Source

Thrown at server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectexport/rule/RuleRepositoryImpl.java:41

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.sonar.api.rule.RuleKey;

import static java.lang.String.format;
import static java.util.Objects.requireNonNull;

public class RuleRepositoryImpl implements RuleRepository {
  private final Map<String, Rule> rulesByUuid = new HashMap<>();

  @Override
  public Rule register(String ref, RuleKey ruleKey) {
    requireNonNull(ruleKey, "ruleKey can not be null");

    Rule rule = rulesByUuid.get(ref);
    if (rule != null) {
      if (!ruleKey.repository().equals(rule.repository()) || !ruleKey.rule().equals(rule.key())) {
        throw new IllegalArgumentException(format(
          "Specified RuleKey '%s' is not equal to the one already registered in repository for ref %s: '%s'",
          ruleKey, ref, RuleKey.of(rule.repository(), rule.key())));
      }
      return rule;
    }

    rule = new Rule(ref, ruleKey.repository(), ruleKey.rule());
    rulesByUuid.put(ref, rule);
    return rule;
  }

  @Override
  public Collection<Rule> getAll() {
    return List.copyOf(rulesByUuid.values());
  }

}

View on GitHub (pinned to 184c821202)

Solutions

  1. Find which step calls register() with the colliding ref and why two distinct RuleKeys map to it
  2. Ensure refs are derived from a unique property of the rule (uuid or full RuleKey) instead of a colliding value
  3. If the ref should map to the same rule, verify the exact repository and rule name spelling (case) matches the earlier registration

Example fix

// before: ref derived from rule name only, can collide across repos
String ref = hash(rule.key());
// after: include repository in ref
String ref = hash(rule.repository() + ":" + rule.key());
Defensive patterns

Strategy: validation

Validate before calling

Rule existing = repositoryLookup(ref);
if (existing != null && !ruleKey.repository().equals(existing.repository())) {
  throw new IllegalArgumentException("ref collision: " + ref);
}

Try / catch

try { ruleRepository.register(ref, ruleKey); } catch (IllegalArgumentException e) { logger.error("Ref {} already maps to a different rule", ref, e); }

Prevention

When it happens

Trigger: Calling register(ref, ruleKey) for a ref that already holds a Rule whose repository() or key() differs from the supplied RuleKey — i.e. a ref/reason collision between two distinct rules during export.

Common situations: A bug in a step computing refs (hash/ID collisions) reusing the same ref for different rules; re-registering rules from different repositories under the same ref during a custom/patched export.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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