{"record":{"id":"8ad0128eb0c5d120","repo":"JakeWharton/butterknife","slug":"no-s-defined-on-s-s-s-s","errorCode":null,"errorMessage":"No @%s defined on @%s's %s.%s.","messagePattern":"No @(.+?) defined on @(.+?)'s (.+?)\\.(.+?)\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"butterknife-compiler/src/main/java/butterknife/compiler/ButterKnifeProcessor.java","lineNumber":1120,"sourceCode":"    ListenerMethod method;\n    ListenerMethod[] methods = listener.method();\n    if (methods.length > 1) {\n      throw new IllegalStateException(String.format(\"Multiple listener methods specified on @%s.\",\n          annotationClass.getSimpleName()));\n    } else if (methods.length == 1) {\n      if (listener.callbacks() != ListenerClass.NONE.class) {\n        throw new IllegalStateException(\n            String.format(\"Both method() and callback() defined on @%s.\",\n                annotationClass.getSimpleName()));\n      }\n      method = methods[0];\n    } else {\n      Method annotationCallback = annotationClass.getDeclaredMethod(\"callback\");\n      Enum<?> callback = (Enum<?>) annotationCallback.invoke(annotation);\n      Field callbackField = callback.getDeclaringClass().getField(callback.name());\n      method = callbackField.getAnnotation(ListenerMethod.class);\n      if (method == null) {\n        throw new IllegalStateException(\n            String.format(\"No @%s defined on @%s's %s.%s.\", ListenerMethod.class.getSimpleName(),\n                annotationClass.getSimpleName(), callback.getDeclaringClass().getSimpleName(),\n                callback.name()));\n      }\n    }\n\n    // Verify that the method has equal to or less than the number of parameters as the listener.\n    List<? extends VariableElement> methodParameters = executableElement.getParameters();\n    if (methodParameters.size() > method.parameters().length) {\n      error(element, \"@%s methods can have at most %s parameter(s). (%s.%s)\",\n          annotationClass.getSimpleName(), method.parameters().length,\n          enclosingElement.getQualifiedName(), element.getSimpleName());\n      hasError = true;\n    }\n\n    // Verify method return type matches the listener.\n    TypeMirror returnType = executableElement.getReturnType();\n    if (returnType instanceof TypeVariable) {","sourceCodeStart":1102,"sourceCodeEnd":1138,"githubUrl":"https://github.com/JakeWharton/butterknife/blob/fcdebedf3276096db2f51bf6372b849b5a9c75ed/butterknife-compiler/src/main/java/butterknife/compiler/ButterKnifeProcessor.java#L1102-L1138","documentation":"When a listener annotation uses the callbacks() shape, the processor reflectively reads the annotation's callback() attribute (an enum constant), looks up the enum constant's Field, and expects an @ListenerMethod annotation on it. If that specific constant lacks @ListenerMethod, this IllegalStateException is thrown. Unlike the BindingSet variant (which walks all constants when generating multi-callback binders), this fires while parsing the single callback the user actually selected.","triggerScenarios":"A callbacks enum where the constant chosen via the annotation's callback() attribute (e.g. @OnItemSelected(callback = Callback.NOTHING_SELECTED)) is missing its @ListenerMethod annotation.","commonSituations":"Extending a copied callback enum with new constants and forgetting the annotation on some; referencing a constant that was added for internal purposes without binding metadata.","solutions":["Add @ListenerMethod(name = ..., parameters = ...) to the enum constant named in the error message","Annotate every constant in the enum so any callback selection is safe"],"exampleFix":"// before\nenum Callback {\n  @ListenerMethod(name = \"onItemSelected\", parameters = {AdapterView.class, View.class, int.class, long.class})\n  ITEM_SELECTED,\n  NOTHING_SELECTED // no annotation\n}\n\n// after\nenum Callback {\n  @ListenerMethod(name = \"onItemSelected\", parameters = {AdapterView.class, View.class, int.class, long.class})\n  ITEM_SELECTED,\n  @ListenerMethod(name = \"onNothingSelected\", parameters = {AdapterView.class})\n  NOTHING_SELECTED\n}","handlingStrategy":"validation","validationCode":"// Ensure the enum constant you select has metadata before using it\nField f = callback.getDeclaringClass().getField(callback.name());\nif (f.getAnnotation(ListenerMethod.class) == null) {\n  throw new IllegalStateException(callback + \" lacks @ListenerMethod\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Annotate every constant in a callbacks enum, not just the ones currently used","Test each callback selection path in a sample project that exercises the processor"],"tags":["butterknife","annotation-processing","listener","custom-annotation"],"backgroundTag":null,"analyzedSha":"fcdebedf3276096db2f51bf6372b849b5a9c75ed","analyzedAt":"2026-08-14T10:13:55.083Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}