SonarSource/sonarqube · error · IllegalArgumentException
Rule ' ' can not use 'Constant/issue' remediation function…
Error message
Rule '%s' can not use 'Constant/issue' remediation function because this rule does not have a fixed remediation cost.
What it means
DebtCalculator.verifyEffortToFix() enforces that a rule using the CONSTANT_ISSUE (Constant/issue) remediation function must NOT have a gap/effortToFix set on the issue. If fn.type() is CONSTANT_ISSUE and issue.gap() != null, it throws IllegalArgumentException because the remediation cost is fixed by definition and a per-issue gap is contradictory.
Solutions
- Change the rule's remediation function from 'Constant/issue' to a gap-based one (e.g. 'Linear/offset' or 'Linear') if per-issue effort is intended
- Remove the gap/effortToFix computation in the sensor/plugin for rules with CONSTANT_ISSUE
- Update the rule metadata (remediationFunction/remediationGap) so it is consistent with what the analyzer reports
Example fix
// before: sensor sets gap on a CONSTANT_ISSUE rule issue.setGap(5.0); // rule fn = CONSTANT_ISSUE -> throws // after option A: drop the gap // issue.gap left unset for constant-cost rules // after option B: use a linear function in the rule definition "remediationFunction": "LINEAR",
Defensive patterns
Strategy: type-guard
Validate before calling
// before invoking debt calculation
DebtRemediationFunction fn = rule.debtRemediationFunction();
if (fn != null && Type.CONSTANT_ISSUE.equals(fn.type()) && issue.gap() != null) {
issue.setGap(null); // or use a linear function rule
} Type guard
static boolean allowsGap(DebtRemediationFunction fn) {
return fn == null || !Type.CONSTANT_ISSUE.equals(fn.type());
}
// guard: if (allowsGap(fn)) { issue.setGap(value); } Try / catch
try {
debt = debtCalculator.calculate(issue, rule);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Constant/issue")) {
issue.setGap(null); // constant-cost rule: gap must be unset
debt = debtCalculator.calculate(issue, rule);
} else throw e;
} Prevention
- Keep rule remediation metadata consistent: CONSTANT_ISSUE rules must never set gap
- Prefer LINEAR / LINEAR_OFFSET functions when the sensor computes per-issue effort
- Audit custom plugins for setGap() calls on constant-cost rules
- Re-check rule definitions after SonarQube/rule-pack upgrades
When it happens
Trigger: calculate() processes an issue whose rule defines remediation function 'Constant/issue' while the analyzer report supplies effortToFix/gap for that issue — e.g. a custom rule or SonarQube plugin computes effort for a rule whose function declares a fixed cost.
Common situations: Custom rule authors setting CONSTANT_ISSUE in the rule definition while their sensor still calls issue.setGap(); rules whose remediation function was changed in a rule update while old issues carry gaps; third-party plugins with mismatched rule metadata.
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
- A removed rule with the key
- A rule with the key ' ' already exists
- Both 'severity' and 'impacts' parameters cannot be set at…
- Cannot resolve issue at line
- Cannot sort on field :
AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09).
Data as JSON: /api/errors/ed035d8cefbc7b7b.
Report an issue: GitHub.
Appendix: source
Thrown at server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/DebtCalculator.java:70
String gapMultiplier = fn.gapMultiplier();
if (fn.type().usesGapMultiplier() && !Strings.isNullOrEmpty(gapMultiplier)) {
int effortToFixValue = MoreObjects.firstNonNull(issue.gap(), 1).intValue();
// TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling
debt = durations.decode(gapMultiplier).multiply(effortToFixValue);
}
String baseEffort = fn.baseEffort();
if (fn.type().usesBaseEffort() && !Strings.isNullOrEmpty(baseEffort)) {
// TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling
debt = debt.add(durations.decode(baseEffort));
}
return debt;
}
return null;
}
private static void verifyEffortToFix(DefaultIssue issue, DebtRemediationFunction fn) {
if (Type.CONSTANT_ISSUE.equals(fn.type()) && issue.gap() != null) {
throw new IllegalArgumentException("Rule '" + issue.getRuleKey() + "' can not use 'Constant/issue' remediation function " +
"because this rule does not have a fixed remediation cost.");
}
}
}
View on GitHub (pinned to 184c821202)