JakeWharton/butterknife · error · IllegalStateException

@%s annotation value() type not int[].

Error message

@%s annotation value() type not int[].

What it means

ButterKnife reads the view IDs for a listener annotation by reflectively invoking the annotation's value() method and casting the result to int[]. If value() is declared with any other return type, the cast would be invalid, so the processor fails fast with this IllegalStateException. It applies to custom listener annotations, since ButterKnife's own annotations all declare int[] value().

Source

Thrown at butterknife-compiler/src/main/java/butterknife/compiler/ButterKnifeProcessor.java:1058

  }

  private void parseListenerAnnotation(Class<? extends Annotation> annotationClass, Element element,
      Map<TypeElement, BindingSet.Builder> builderMap, Set<TypeElement> erasedTargetNames)
      throws Exception {
    // This should be guarded by the annotation's @Target but it's worth a check for safe casting.
    if (!(element instanceof ExecutableElement) || element.getKind() != METHOD) {
      throw new IllegalStateException(
          String.format("@%s annotation must be on a method.", annotationClass.getSimpleName()));
    }

    ExecutableElement executableElement = (ExecutableElement) element;
    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

    // Assemble information on the method.
    Annotation annotation = element.getAnnotation(annotationClass);
    Method annotationValue = annotationClass.getDeclaredMethod("value");
    if (annotationValue.getReturnType() != int[].class) {
      throw new IllegalStateException(
          String.format("@%s annotation value() type not int[].", annotationClass));
    }

    int[] ids = (int[]) annotationValue.invoke(annotation);
    String name = executableElement.getSimpleName().toString();
    boolean required = isListenerRequired(executableElement);

    // Verify that the method and its containing class are accessible via generated code.
    boolean hasError = isInaccessibleViaGeneratedCode(annotationClass, "methods", element);
    hasError |= isBindingInWrongPackage(annotationClass, element);

    Integer duplicateId = findDuplicate(ids);
    if (duplicateId != null) {
      error(element, "@%s annotation for method contains duplicate ID %d. (%s.%s)",
          annotationClass.getSimpleName(), duplicateId, enclosingElement.getQualifiedName(),
          element.getSimpleName());
      hasError = true;
    }

View on GitHub (pinned to fcdebedf32)

Solutions

  1. Change the annotation's value() declaration to `int[] value() default {};`
  2. Use the wizardry-free route: copy the declaration shape of butterknife.OnClick as the template for custom listener annotations

Example fix

// before
public @interface OnFoo {
  int value(); // single int
}

// after
public @interface OnFoo {
  int[] value() default { NO_ID.value }; // or just int[] value();
}
Defensive patterns

Strategy: validation

Validate before calling

// Fail fast in a test that the contract holds
Method m = OnCustomEvent.class.getDeclaredMethod("value");
assert m.getReturnType() == int[].class : "value() must return int[]";

Prevention

When it happens

Trigger: Creating a custom listener annotation meta-annotated with @ListenerClass whose value() returns int, Integer[], long[], or takes parameters; renaming the ID attribute so value() no longer exists would instead raise NoSuchMethodException from getDeclaredMethod before this check in some flows.

Common situations: Hand-writing a custom @ListenerClass annotation and modeling value() on single-ID annotations like @BindBool (which takes one int) instead of multi-ID listener annotations.

Related errors


AI-assisted analysis of JakeWharton/butterknife@fcdebedf32 (2026-08-14). Data as JSON: /api/errors/162188006cf8dd72. Report an issue: GitHub.