{"record":{"id":"87084a2bc289a6c0","repo":"JetBrains/intellij-community","slug":"changesignature-contract-converter-invalid-clause","errorCode":"changeSignature.contract.converter.invalid.clause","errorMessage":"Invalid contract clause '{0}'","messagePattern":"Invalid contract clause '(.+?)'","errorType":"exception","errorClass":"ContractConversionException","httpStatus":null,"severity":"error","filePath":"java/java-impl/src/com/intellij/refactoring/changeSignature/ContractConverter.java","lineNumber":73,"sourceCode":"    }\n    int[] newToOldIndex = StreamEx.of(newParameters).mapToInt(ParameterInfo::getOldIndex).toArray();\n    int[] oldToNewIndex = reverseIndex(oldParameterNames.length, newToOldIndex);\n\n    List<StandardMethodContract> result = new ArrayList<>();\n    for (StandardMethodContract contract : contracts) {\n      result.add(convertContract(contract, newToOldIndex, oldToNewIndex, oldParameterNames));\n    }\n    if (result.equals(contracts)) return annotation;\n    return JavaMethodContractUtil.updateContract(annotation, result);\n  }\n\n  private static @NotNull StandardMethodContract convertContract(@NotNull StandardMethodContract contract,\n                                                                 int @NotNull [] newToOldIndex,\n                                                                 int @NotNull [] oldToNewIndex,\n                                                                 String @NotNull [] oldParameterNames) throws ContractConversionException {\n    if (contract.getParameterCount() != oldToNewIndex.length) {\n      // invalid contract\n      throw new ContractConversionException(JavaRefactoringBundle.message(\"changeSignature.contract.converter.invalid.clause\", contract));\n    }\n    for (int i = 0; i < contract.getParameterCount(); i++) {\n      if (contract.getParameterConstraint(i) != StandardMethodContract.ValueConstraint.ANY_VALUE && oldToNewIndex[i] == -1) {\n        String paramRemovedMessage = JavaRefactoringBundle.message(\"changeSignature.contract.converter.parameter.removed\",\n                                                                   oldParameterNames[i], contract);\n        throw new ContractConversionException(paramRemovedMessage);\n      }\n    }\n    StandardMethodContract.ValueConstraint[] newConstraints = IntStreamEx.of(newToOldIndex)\n                                                                         .mapToObj(idx -> idx == -1 ? StandardMethodContract.ValueConstraint.ANY_VALUE : contract.getParameterConstraint(idx))\n                                                                         .toArray(StandardMethodContract.ValueConstraint.class);\n    ContractReturnValue returnValue = contract.getReturnValue();\n    if (returnValue instanceof ContractReturnValue.ParameterReturnValue) {\n      int oldIndex = ((ContractReturnValue.ParameterReturnValue)returnValue).getParameterNumber();\n      if (oldIndex >= contract.getParameterCount()) {\n        String errorRefMessage = JavaRefactoringBundle.message(\"changeSignature.contract.converter.invalid.return.reference\", returnValue);\n        throw new ContractConversionException(errorRefMessage);\n      }","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/JetBrains/intellij-community/blob/be881553f2a76ac8b4ea53950d93f0de78295c19/java/java-impl/src/com/intellij/refactoring/changeSignature/ContractConverter.java#L55-L91","documentation":"Thrown by the private convertContract(StandardMethodContract, ...) in ContractConverter when a parsed contract clause's parameter count does not match the number of parameters in the old signature being refactored (contract.getParameterCount() != oldToNewIndex.length). The clause cannot be mapped onto the new parameter list, so Change Signature aborts with ContractConversionException.","triggerScenarios":"Running Change Signature on a method where each @Contract clause lists more or fewer comma-separated constraints than the method currently has parameters — typically because parameters were already added/removed from the method (or an override) without updating the annotation.","commonSituations":"Method signature edited (parameter added/removed) while @Contract kept the old arity; different overrides of the same hierarchy carrying contracts of mismatched arity; generated code where contract strings are stale.","solutions":["Update the @Contract value so every clause has exactly one constraint per parameter of the current signature, using '_' for don't-care positions.","If parameters were added, extend each clause, e.g. two-param 'null, _ -> fail' becomes 'null, _, _ -> fail' after adding a third parameter.","Re-run Change Signature after the annotation matches the current signature."],"exampleFix":"// before (3 params, 2-constraint clause)\n@Contract(\"null, _ -> fail\")\nvoid f(String a, Object b, Object c) { ... }\n// after\n@Contract(\"null, _, _ -> fail\")\nvoid f(String a, Object b, Object c) { ... }","handlingStrategy":"validation","validationCode":"String text = AnnotationUtil.getStringAttributeValue(annotation, null);\nif (text != null) {\n  for (StandardMethodContract c : StandardMethodContract.parseContract(text)) {\n    if (c.getParameterCount() != method.getParameterList().getParametersCount()) {\n      // clause arity mismatch; fix annotation before Change Signature\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try { ContractConverter.convertContract(method, info); } catch (ContractConversionException e) { /* align each clause's constraint count with parameter count, retry */ }","preventionTips":["Keep exactly one constraint per parameter per clause; use '_' for don't-care.","Whenever you add/remove a parameter, update all clauses of the @Contract in the same commit.","Add a CI grep/inspection that flags @Contract arity drift on annotated methods."],"tags":["java","refactoring","change-signature","contract-annotation","arity"],"backgroundTag":null,"analyzedSha":"be881553f2a76ac8b4ea53950d93f0de78295c19","analyzedAt":"2026-08-14T14:13:06.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}