JakeWharton/butterknife · error · IllegalStateException
@%s annotation must be on a method.
Error message
@%s annotation must be on a method.
What it means
ButterKnife's processor only accepts listener annotations (e.g. @OnClick, @OnCheckedChanged, or any annotation meta-annotated with @ListenerClass) on methods. Before casting the element to ExecutableElement it verifies the element is an executable of kind METHOD; otherwise this IllegalStateException is thrown. Although @Target(METHOD) normally prevents this, the processor keeps the check for safe casting.
Source
Thrown at butterknife-compiler/src/main/java/butterknife/compiler/ButterKnifeProcessor.java:1047
if (!SuperficialValidation.validateElement(element)) continue;
try {
parseListenerAnnotation(annotationClass, element, builderMap, erasedTargetNames);
} catch (Exception e) {
StringWriter stackTrace = new StringWriter();
e.printStackTrace(new PrintWriter(stackTrace));
error(element, "Unable to generate view binder for @%s.\n\n%s",
annotationClass.getSimpleName(), stackTrace.toString());
}
}
}
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);
View on GitHub (pinned to fcdebedf32)
Solutions
- Move the annotation onto a method (e.g. a public void onButtonClick(View v) method in the activity/fragment)
- For field-style injection use @BindView instead of a listener annotation
- If you own the annotation, declare @Target(ElementType.METHOD) on it so the compiler rejects misuse earlier
Example fix
// before
@OnClick(R.id.submit)
Button submitButton; // annotation on a field
// after
@BindView(R.id.submit)
Button submitButton;
@OnClick(R.id.submit)
void onSubmitClicked(View v) { ... } Defensive patterns
Strategy: validation
Type guard
// Annotation declaration itself guards usage — restrict targets
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface OnCustomEvent { int[] value(); } Prevention
- Declare @Target(ElementType.METHOD) on every custom listener annotation
- Keep listener annotations on methods and field annotations on fields
- Run a full build after moving annotations so the processor reports misuse immediately
When it happens
Trigger: Applying a listener annotation to a field, constructor, or class in a project where the annotation's @Target allows more than METHOD; using a custom listener annotation declared with @Target({FIELD, METHOD}); applying @OnClick to a Kotlin property backing field exposed as a field element.
Common situations: Kotlin code where @OnClick is placed on a property or the annotation is attached in Java to a field by accident; custom listener annotations copied from samples with a loose @Target.
Related errors
- @%s's %s.%s missing @%s annotation.
- Multiple listener methods specified on @%s.
- Both method() and callback() defined on @%s.
- No @%s defined on @%s's %s.%s.
- @%s annotation value() type not int[].
AI-assisted analysis of JakeWharton/butterknife@fcdebedf32 (2026-08-14).
Data as JSON: /api/errors/25303dc0b908acaa.
Report an issue: GitHub.