{"record":{"id":"133c890f05c1712e","repo":"bazelbuild/bazel","slug":"class-s-has-an-incompatible-overload-of-annotated","errorCode":null,"errorMessage":"Class %s has an incompatible overload of annotated method %s declared by %s","messagePattern":"Class (.+?) has an incompatible overload of annotated method (.+?) declared by (.+?)","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/net/starlark/java/annot/StarlarkAnnotations.java","lineNumber":225,"sourceCode":"    // invariants should be verified in annotation processor or in test, and left out of this\n    // method.\n    Method[] methods = classObj.getDeclaredMethods();\n    Class<?>[] paramsToMatch = signatureToMatch.getParameterTypes();\n\n    StarlarkMethod callable = null;\n\n    for (Method method : methods) {\n      if (signatureToMatch.getName().equals(method.getName())\n          && method.isAnnotationPresent(StarlarkMethod.class)) {\n        Class<?>[] paramTypes = method.getParameterTypes();\n\n        if (paramTypes.length == paramsToMatch.length) {\n          for (int i = 0; i < paramTypes.length; i++) {\n            // This verifies assignability of the method signature to ensure this is not a\n            // coincidental overload. We verify assignability instead of matching exact parameter\n            // classes in order to match generic methods.\n            if (!paramTypes[i].isAssignableFrom(paramsToMatch[i])) {\n              throw new IllegalStateException(\n                  String.format(\n                      \"Class %s has an incompatible overload of annotated method %s declared by %s\",\n                      classObj, signatureToMatch.getName(), signatureToMatch.getDeclaringClass()));\n            }\n          }\n        }\n        if (callable == null) {\n          callable = method.getAnnotation(StarlarkMethod.class);\n        } else {\n          throw new IllegalStateException(\n              String.format(\n                  \"Class %s has multiple overloaded methods named '%s' annotated \"\n                      + \"with @StarlarkMethod\",\n                  classObj, signatureToMatch.getName()));\n        }\n      }\n    }\n    return callable;","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/bazelbuild/bazel/blob/e6e199d0601a244511b4cf18c8b2828aa73db1fd/src/main/java/net/starlark/java/annot/StarlarkAnnotations.java#L207-L243","documentation":"While collecting @StarlarkMethod annotations along a class hierarchy, StarlarkAnnotations found a method in a subclass with the same name and parameter count as an inherited annotated method, but a parameter type is not assignable — i.e. a genuine (not erased/generic-compatible) overload of an annotated method. Overloading annotated Starlark methods this way is unsupported, so an IllegalStateException is thrown at descriptor-build time.","triggerScenarios":"Subclass declares void m(OtherType x) with @StarlarkMethod while superclass has @StarlarkMethod void m(MyType x); the assignability check paramTypes[i].isAssignableFrom(paramsToMatch[i]) fails for some parameter.","commonSituations":"Refactoring an exported Starlark method's parameter type in a base class without updating subclasses; adding a convenience overload that coincidentally carries the annotation via copy-paste; generics erasure producing a bridge with incompatible raw types.","solutions":["Make the subclass override use identical (or assignable-to-super) parameter types.","Rename the subclass method if it is meant to be a distinct operation, not an override.","Remove @StarlarkMethod from the accidental overload.","If a different signature is required, expose it under a different Starlark method name via structType/name attribute."],"exampleFix":"// before\nclass Base { @StarlarkMethod(name=\"merge\") public void m(Dict<?,?> d) {} }\nclass Sub extends Base {\n  @StarlarkMethod(name=\"merge\") public void m(Sequence<?> s) {} // incompatible overload\n}\n\n// after\nclass Sub extends Base {\n  @StarlarkMethod(name=\"merge_seq\") public void m(Sequence<?> s) {} // distinct name\n}","handlingStrategy":"validation","validationCode":"// In a build-time test, assert no incompatible annotated overloads exist\nstatic void checkNoIncompatibleOverloads(Class<?> c) {\n  for (Method m : c.getDeclaredMethods()) {\n    if (!m.isAnnotationPresent(StarlarkMethod.class)) continue;\n    for (Method sup : c.getSuperclass().getMethods()) {\n      if (sup.getName().equals(m.getName())\n          && sup.isAnnotationPresent(StarlarkMethod.class)\n          && sup.getParameterCount() == m.getParameterCount()) {\n        for (int i = 0; i < m.getParameterCount(); i++)\n          if (!m.getParameterTypes()[i].isAssignableFrom(sup.getParameterTypes()[i]))\n            throw new AssertionError(m + ' incompatible with inherited ' + sup);\n      }\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When overriding an exported method, copy the exact parameter types from the base declaration.","Give genuinely different operations different Starlark names.","Add a reflection-based consistency test for Starlark-exposed classes."],"tags":["starlark","annotations","method-resolution","java"],"backgroundTag":null,"analyzedSha":"e6e199d0601a244511b4cf18c8b2828aa73db1fd","analyzedAt":"2026-08-14T10:24:27.848Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}