JetBrains/intellij-community · error · ContractConversionException

changeSignature.contract.converter.mutation.contract

changeSignature.contract.converter.mutation.contract

Error message

Annotation contains mutation contract

What it means

Thrown by ContractConverter.convertContract when the method's @Contract annotation also declares the 'mutates' attribute (org.jetbrains.annotations.MutationSignature ATTR_MUTATES, used by @MutationContract-style contracts). The converter only knows how to rewrite value contracts, not mutation contracts that reference parameters by name for mutation semantics, so it aborts with ContractConversionException.

Source

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

  private ContractConverter() {}

  public static @Nullable PsiAnnotation convertContract(@NotNull PsiMethod method, @NotNull JavaChangeInfo info) throws ContractConversionException {
    return convertContract(method, info.getOldParameterNames(), info.getNewParameters());
  }

  public static @Nullable PsiAnnotation convertContract(@NotNull PsiMethod method,
                                                        String @NotNull [] oldParameterNames,
                                                        JavaParameterInfo @NotNull [] newParameters) throws ContractConversionException {
    PsiAnnotation annotation = JavaMethodContractUtil.findContractAnnotation(method);
    if (annotation == null || AnnotationUtil.isInferredAnnotation(annotation)) return null;
    if (AnnotationUtil.isExternalAnnotation(annotation)) {
      throw new ContractConversionException(JavaRefactoringBundle.message("changeSignature.contract.converter.external.annotations"));
    }
    if (annotation.getOwner() != method.getModifierList()) {
      throw new ContractInheritedException();
    }
    if (annotation.findDeclaredAttributeValue(MutationSignature.ATTR_MUTATES) != null) {
      throw new ContractConversionException(JavaRefactoringBundle.message("changeSignature.contract.converter.mutation.contract"));
    }
    String text = AnnotationUtil.getStringAttributeValue(annotation, null);
    List<StandardMethodContract> contracts = Collections.emptyList();
    if (text != null) {
      try {
        contracts = StandardMethodContract.parseContract(text);
      }
      catch (StandardMethodContract.ParseException exception) {
        String definitionError = JavaRefactoringBundle.message("changeSignature.contract.converter.definition.error",
                                                               exception.getMessage());
        throw new ContractConversionException(definitionError);
      }
    }
    int[] newToOldIndex = StreamEx.of(newParameters).mapToInt(ParameterInfo::getOldIndex).toArray();
    int[] oldToNewIndex = reverseIndex(oldParameterNames.length, newToOldIndex);

    List<StandardMethodContract> result = new ArrayList<>();
    for (StandardMethodContract contract : contracts) {

View on GitHub (pinned to be881553f2)

Solutions

  1. Temporarily remove the mutates attribute from the @Contract annotation, run Change Signature, then re-add mutates with updated parameter references.
  2. Update the mutation contract manually after performing the signature change without automatic conversion.
  3. Keep parameter order stable for methods with mutation contracts, or refactor them by hand.

Example fix

// before
@Contract(mutates = "param1", value = "null -> false")
boolean process(String a, List<String> out) { ... }
// after (manual, params reordered)
@Contract(mutates = "param2", value = "null -> false")
boolean process(List<String> out, String a) { ... }
Defensive patterns

Strategy: validation

Validate before calling

PsiAnnotation a = JavaMethodContractUtil.findContractAnnotation(method);
if (a != null && a.findDeclaredAttributeValue("mutates") != null) {
  // mutation contract present; plan a manual contract update around the refactoring
}

Try / catch

try { ContractConverter.convertContract(method, info); } catch (ContractConversionException e) { /* if message mentions mutation contract, edit annotation manually then retry */ }

Prevention

When it happens

Trigger: Running Change Signature on a method annotated like @Contract(mutates = "this,param1", value = "...") or any contract whose attribute set includes mutates; the signature change would require reindexing the mutation contract's parameter references.

Common situations: Code using JetBrains mutation-contract annotations on builder/mutator methods; refactoring methods annotated with both value and mutates; recent IDE versions that parse mutates make previously ignorable cases fatal.

Related errors


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