bazelbuild/bazel · error · IllegalStateException

Class %s has an incompatible overload of annotated method %s

Error message

Class %s has an incompatible overload of annotated method %s declared by %s

What it means

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.

Source

Thrown at src/main/java/net/starlark/java/annot/StarlarkAnnotations.java:225

    // invariants should be verified in annotation processor or in test, and left out of this
    // method.
    Method[] methods = classObj.getDeclaredMethods();
    Class<?>[] paramsToMatch = signatureToMatch.getParameterTypes();

    StarlarkMethod callable = null;

    for (Method method : methods) {
      if (signatureToMatch.getName().equals(method.getName())
          && method.isAnnotationPresent(StarlarkMethod.class)) {
        Class<?>[] paramTypes = method.getParameterTypes();

        if (paramTypes.length == paramsToMatch.length) {
          for (int i = 0; i < paramTypes.length; i++) {
            // This verifies assignability of the method signature to ensure this is not a
            // coincidental overload. We verify assignability instead of matching exact parameter
            // classes in order to match generic methods.
            if (!paramTypes[i].isAssignableFrom(paramsToMatch[i])) {
              throw new IllegalStateException(
                  String.format(
                      "Class %s has an incompatible overload of annotated method %s declared by %s",
                      classObj, signatureToMatch.getName(), signatureToMatch.getDeclaringClass()));
            }
          }
        }
        if (callable == null) {
          callable = method.getAnnotation(StarlarkMethod.class);
        } else {
          throw new IllegalStateException(
              String.format(
                  "Class %s has multiple overloaded methods named '%s' annotated "
                      + "with @StarlarkMethod",
                  classObj, signatureToMatch.getName()));
        }
      }
    }
    return callable;

View on GitHub (pinned to e6e199d060)

Solutions

  1. Make the subclass override use identical (or assignable-to-super) parameter types.
  2. Rename the subclass method if it is meant to be a distinct operation, not an override.
  3. Remove @StarlarkMethod from the accidental overload.
  4. If a different signature is required, expose it under a different Starlark method name via structType/name attribute.

Example fix

// before
class Base { @StarlarkMethod(name="merge") public void m(Dict<?,?> d) {} }
class Sub extends Base {
  @StarlarkMethod(name="merge") public void m(Sequence<?> s) {} // incompatible overload
}

// after
class Sub extends Base {
  @StarlarkMethod(name="merge_seq") public void m(Sequence<?> s) {} // distinct name
}
Defensive patterns

Strategy: validation

Validate before calling

// In a build-time test, assert no incompatible annotated overloads exist
static void checkNoIncompatibleOverloads(Class<?> c) {
  for (Method m : c.getDeclaredMethods()) {
    if (!m.isAnnotationPresent(StarlarkMethod.class)) continue;
    for (Method sup : c.getSuperclass().getMethods()) {
      if (sup.getName().equals(m.getName())
          && sup.isAnnotationPresent(StarlarkMethod.class)
          && sup.getParameterCount() == m.getParameterCount()) {
        for (int i = 0; i < m.getParameterCount(); i++)
          if (!m.getParameterTypes()[i].isAssignableFrom(sup.getParameterTypes()[i]))
            throw new AssertionError(m + ' incompatible with inherited ' + sup);
      }
    }
  }
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of bazelbuild/bazel@e6e199d060 (2026-08-14). Data as JSON: /api/errors/133c890f05c1712e. Report an issue: GitHub.