{"record":{"id":"25303dc0b908acaa","repo":"JakeWharton/butterknife","slug":"s-annotation-must-be-on-a-method","errorCode":null,"errorMessage":"@%s annotation must be on a method.","messagePattern":"@(.+?) annotation must be on a method\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"butterknife-compiler/src/main/java/butterknife/compiler/ButterKnifeProcessor.java","lineNumber":1047,"sourceCode":"      if (!SuperficialValidation.validateElement(element)) continue;\n      try {\n        parseListenerAnnotation(annotationClass, element, builderMap, erasedTargetNames);\n      } catch (Exception e) {\n        StringWriter stackTrace = new StringWriter();\n        e.printStackTrace(new PrintWriter(stackTrace));\n\n        error(element, \"Unable to generate view binder for @%s.\\n\\n%s\",\n            annotationClass.getSimpleName(), stackTrace.toString());\n      }\n    }\n  }\n\n  private void parseListenerAnnotation(Class<? extends Annotation> annotationClass, Element element,\n      Map<TypeElement, BindingSet.Builder> builderMap, Set<TypeElement> erasedTargetNames)\n      throws Exception {\n    // This should be guarded by the annotation's @Target but it's worth a check for safe casting.\n    if (!(element instanceof ExecutableElement) || element.getKind() != METHOD) {\n      throw new IllegalStateException(\n          String.format(\"@%s annotation must be on a method.\", annotationClass.getSimpleName()));\n    }\n\n    ExecutableElement executableElement = (ExecutableElement) element;\n    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();\n\n    // Assemble information on the method.\n    Annotation annotation = element.getAnnotation(annotationClass);\n    Method annotationValue = annotationClass.getDeclaredMethod(\"value\");\n    if (annotationValue.getReturnType() != int[].class) {\n      throw new IllegalStateException(\n          String.format(\"@%s annotation value() type not int[].\", annotationClass));\n    }\n\n    int[] ids = (int[]) annotationValue.invoke(annotation);\n    String name = executableElement.getSimpleName().toString();\n    boolean required = isListenerRequired(executableElement);\n","sourceCodeStart":1029,"sourceCodeEnd":1065,"githubUrl":"https://github.com/JakeWharton/butterknife/blob/fcdebedf3276096db2f51bf6372b849b5a9c75ed/butterknife-compiler/src/main/java/butterknife/compiler/ButterKnifeProcessor.java#L1029-L1065","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n@OnClick(R.id.submit)\nButton submitButton; // annotation on a field\n\n// after\n@BindView(R.id.submit)\nButton submitButton;\n\n@OnClick(R.id.submit)\nvoid onSubmitClicked(View v) { ... }","handlingStrategy":"validation","validationCode":null,"typeGuard":"// Annotation declaration itself guards usage — restrict targets\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.CLASS)\npublic @interface OnCustomEvent { int[] value(); }","tryCatchPattern":null,"preventionTips":["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"],"tags":["butterknife","annotation-processing","listener","misuse"],"backgroundTag":null,"analyzedSha":"fcdebedf3276096db2f51bf6372b849b5a9c75ed","analyzedAt":"2026-08-14T10:13:55.083Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}