JetBrains/intellij-community · error · ContractConversionException

changeSignature.contract.converter.invalid.clause

changeSignature.contract.converter.invalid.clause

Error message

Invalid contract clause '{0}'

What it means

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.

Source

Thrown at java/java-impl/src/com/intellij/refactoring/changeSignature/ContractConverter.java:73

    }
    int[] newToOldIndex = StreamEx.of(newParameters).mapToInt(ParameterInfo::getOldIndex).toArray();
    int[] oldToNewIndex = reverseIndex(oldParameterNames.length, newToOldIndex);

    List<StandardMethodContract> result = new ArrayList<>();
    for (StandardMethodContract contract : contracts) {
      result.add(convertContract(contract, newToOldIndex, oldToNewIndex, oldParameterNames));
    }
    if (result.equals(contracts)) return annotation;
    return JavaMethodContractUtil.updateContract(annotation, result);
  }

  private static @NotNull StandardMethodContract convertContract(@NotNull StandardMethodContract contract,
                                                                 int @NotNull [] newToOldIndex,
                                                                 int @NotNull [] oldToNewIndex,
                                                                 String @NotNull [] oldParameterNames) throws ContractConversionException {
    if (contract.getParameterCount() != oldToNewIndex.length) {
      // invalid contract
      throw new ContractConversionException(JavaRefactoringBundle.message("changeSignature.contract.converter.invalid.clause", contract));
    }
    for (int i = 0; i < contract.getParameterCount(); i++) {
      if (contract.getParameterConstraint(i) != StandardMethodContract.ValueConstraint.ANY_VALUE && oldToNewIndex[i] == -1) {
        String paramRemovedMessage = JavaRefactoringBundle.message("changeSignature.contract.converter.parameter.removed",
                                                                   oldParameterNames[i], contract);
        throw new ContractConversionException(paramRemovedMessage);
      }
    }
    StandardMethodContract.ValueConstraint[] newConstraints = IntStreamEx.of(newToOldIndex)
                                                                         .mapToObj(idx -> idx == -1 ? StandardMethodContract.ValueConstraint.ANY_VALUE : contract.getParameterConstraint(idx))
                                                                         .toArray(StandardMethodContract.ValueConstraint.class);
    ContractReturnValue returnValue = contract.getReturnValue();
    if (returnValue instanceof ContractReturnValue.ParameterReturnValue) {
      int oldIndex = ((ContractReturnValue.ParameterReturnValue)returnValue).getParameterNumber();
      if (oldIndex >= contract.getParameterCount()) {
        String errorRefMessage = JavaRefactoringBundle.message("changeSignature.contract.converter.invalid.return.reference", returnValue);
        throw new ContractConversionException(errorRefMessage);
      }

View on GitHub (pinned to be881553f2)

Solutions

  1. Update the @Contract value so every clause has exactly one constraint per parameter of the current signature, using '_' for don't-care positions.
  2. If parameters were added, extend each clause, e.g. two-param 'null, _ -> fail' becomes 'null, _, _ -> fail' after adding a third parameter.
  3. Re-run Change Signature after the annotation matches the current signature.

Example fix

// before (3 params, 2-constraint clause)
@Contract("null, _ -> fail")
void f(String a, Object b, Object c) { ... }
// after
@Contract("null, _, _ -> fail")
void f(String a, Object b, Object c) { ... }
Defensive patterns

Strategy: validation

Validate before calling

String text = AnnotationUtil.getStringAttributeValue(annotation, null);
if (text != null) {
  for (StandardMethodContract c : StandardMethodContract.parseContract(text)) {
    if (c.getParameterCount() != method.getParameterList().getParametersCount()) {
      // clause arity mismatch; fix annotation before Change Signature
    }
  }
}

Try / catch

try { ContractConverter.convertContract(method, info); } catch (ContractConversionException e) { /* align each clause's constraint count with parameter count, retry */ }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of JetBrains/intellij-community@be881553f2 (2026-08-14). Data as JSON: /api/errors/87084a2bc289a6c0. Report an issue: GitHub.