JetBrains/intellij-community · error · ContractConversionException
changeSignature.contract.converter.external.annotations
changeSignature.contract.converter.external.annotations
Error message
Automatic update of external annotation is not supported
What it means
Thrown by ContractConverter.convertContract during Change Signature refactoring when a method's @org.jetbrains.annotations.Contract annotation is an external annotation (stored in an external annotations XML file rather than in the source). The IDE cannot automatically update external annotations, so the refactoring fails with ContractConversionException instead of silently leaving a stale contract.
Source
Thrown at java/java-impl/src/com/intellij/refactoring/changeSignature/ContractConverter.java:36
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public final class ContractConverter {
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);
}View on GitHub (pinned to be881553f2)
Solutions
- Move the @Contract annotation into the source file (remove it from external annotations XML, add it above the method) and rerun Change Signature.
- Manually update the contract clause in the external annotations XML to match the new parameter order, then skip automatic conversion.
- Perform the signature change without contract conversion and fix the contract yourself afterwards.
- Exclude the annotated method from refactoring or revert parameter reordering that affects contract clauses.
Example fix
// before: external annotations XML
<method ...><annotation name='org.jetbrains.annotations.Contract'><val name='value' val='null -> false'/></annotation></method>
// after: inline in source
import org.jetbrains.annotations.Contract;
@Contract("null -> false")
public boolean check(@Nullable String s) { ... } Defensive patterns
Strategy: try-catch
Validate before calling
PsiAnnotation ann = JavaMethodContractUtil.findContractAnnotation(method);
if (ann != null && AnnotationUtil.isExternalAnnotation(ann)) {
// update external annotations XML manually or inline the annotation first
} Type guard
static boolean isInlineContract(@NotNull PsiMethod m) {
PsiAnnotation a = JavaMethodContractUtil.findContractAnnotation(m);
return a == null || (!AnnotationUtil.isExternalAnnotation(a) && !AnnotationUtil.isInferredAnnotation(a) && a.getOwner() == m.getModifierList());
} Try / catch
try { ContractConverter.convertContract(method, info); } catch (ContractConversionException e) { /* show message; for external annotations update the XML by hand */ } Prevention
- Keep @Contract annotations inline in sources for methods you plan to refactor.
- Before Change Signature, check whether the contract comes from external annotations (annotation gutter icon / AnnotationUtil.isExternalAnnotation).
- Track external annotation XML in VCS and update it together with signature changes.
When it happens
Trigger: Running Change Signature on a method whose @Contract lives in external annotations (annotations/.../methods.xml next to a library, or an 'external annotations' directory configured in the module) while the signature change would require updating the contract.
Common situations: Projects that attach external annotations to third-party or read-only sources; libraries annotated via JetBrains external annotation sets; code where methods are annotated through an annotations repository rather than inline.
Related errors
- changeSignature.contract.converter.inherited.annotation
- changeSignature.contract.converter.mutation.contract
- changeSignature.contract.converter.definition.error
- changeSignature.contract.converter.invalid.clause
- changeSignature.contract.converter.parameter.removed
AI-assisted analysis of JetBrains/intellij-community@be881553f2 (2026-08-14).
Data as JSON: /api/errors/be20fb5ac14d19a4.
Report an issue: GitHub.