{"record":{"id":"ed035d8cefbc7b7b","repo":"SonarSource/sonarqube","slug":"rule-s-can-not-use-constant-issue-remediation","errorCode":null,"errorMessage":"Rule '%s' can not use 'Constant/issue' remediation function because this rule does not have a fixed remediation cost.","messagePattern":"Rule '(.+?)' can not use 'Constant/issue' remediation function because this rule does not have a fixed remediation cost\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/DebtCalculator.java","lineNumber":70,"sourceCode":"      String gapMultiplier = fn.gapMultiplier();\n      if (fn.type().usesGapMultiplier() && !Strings.isNullOrEmpty(gapMultiplier)) {\n        int effortToFixValue = MoreObjects.firstNonNull(issue.gap(), 1).intValue();\n        // TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling\n        debt = durations.decode(gapMultiplier).multiply(effortToFixValue);\n      }\n      String baseEffort = fn.baseEffort();\n      if (fn.type().usesBaseEffort() && !Strings.isNullOrEmpty(baseEffort)) {\n        // TODO convert to Duration directly in Rule#remediationFunction -> better performance + error handling\n        debt = debt.add(durations.decode(baseEffort));\n      }\n      return debt;\n    }\n    return null;\n  }\n\n  private static void verifyEffortToFix(DefaultIssue issue, DebtRemediationFunction fn) {\n    if (Type.CONSTANT_ISSUE.equals(fn.type()) && issue.gap() != null) {\n      throw new IllegalArgumentException(\"Rule '\" + issue.getRuleKey() + \"' can not use 'Constant/issue' remediation function \" +\n        \"because this rule does not have a fixed remediation cost.\");\n    }\n  }\n}\n","sourceCodeStart":52,"sourceCodeEnd":75,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/DebtCalculator.java#L52-L75","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: sensor sets gap on a CONSTANT_ISSUE rule\nissue.setGap(5.0); // rule fn = CONSTANT_ISSUE -> throws\n// after option A: drop the gap\n// issue.gap left unset for constant-cost rules\n// after option B: use a linear function in the rule definition\n\"remediationFunction\": \"LINEAR\",","handlingStrategy":"type-guard","validationCode":"// before invoking debt calculation\nDebtRemediationFunction fn = rule.debtRemediationFunction();\nif (fn != null && Type.CONSTANT_ISSUE.equals(fn.type()) && issue.gap() != null) {\n  issue.setGap(null); // or use a linear function rule\n}","typeGuard":"static boolean allowsGap(DebtRemediationFunction fn) {\n  return fn == null || !Type.CONSTANT_ISSUE.equals(fn.type());\n}\n// guard: if (allowsGap(fn)) { issue.setGap(value); }","tryCatchPattern":"try {\n  debt = debtCalculator.calculate(issue, rule);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"Constant/issue\")) {\n    issue.setGap(null); // constant-cost rule: gap must be unset\n    debt = debtCalculator.calculate(issue, rule);\n  } else throw e;\n}","preventionTips":["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"],"tags":["issues","debt","remediation","rules"],"backgroundTag":"invalid-argument-value","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"}