JakeWharton/butterknife · error · IllegalStateException
No @%s defined on @%s.
Error message
No @%s defined on @%s.
What it means
The processor recognizes a listener annotation by looking for the @ListenerClass meta-annotation on it, which supplies the target setter method, listener type, and so on. If a listener annotation is processed (typically because it was placed on a method and reached parseListenerAnnotation) but carries no @ListenerClass, this IllegalStateException is thrown. It means the annotation looks like a ButterKnife listener but lacks the metadata the processor needs.
Source
Thrown at butterknife-compiler/src/main/java/butterknife/compiler/ButterKnifeProcessor.java:1080
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;
}
ListenerClass listener = annotationClass.getAnnotation(ListenerClass.class);
if (listener == null) {
throw new IllegalStateException(
String.format("No @%s defined on @%s.", ListenerClass.class.getSimpleName(),
annotationClass.getSimpleName()));
}
for (int id : ids) {
if (id == NO_ID.value) {
if (ids.length == 1) {
if (!required) {
error(element, "ID-free binding must not be annotated with @Optional. (%s.%s)",
enclosingElement.getQualifiedName(), element.getSimpleName());
hasError = true;
}
} else {
error(element, "@%s annotation contains invalid ID %d. (%s.%s)",
annotationClass.getSimpleName(), id, enclosingElement.getQualifiedName(),
element.getSimpleName());
hasError = true;
}View on GitHub (pinned to fcdebedf32)
Solutions
- Add @ListenerClass(...) to the annotation with targetType, setter, type, and either method or callbacks filled in
- If the annotation is not meant to be a ButterKnife listener, remove it from the method or from the set of annotations processed by ButterKnifeProcessor
Example fix
// before
public @interface OnCustomEvent {
int[] value();
}
// after
@ListenerClass(
targetType = "android.widget.CustomView",
setter = "setCustomListener",
type = "com.example.CustomListener",
method = @ListenerMethod(name = "onCustom", parameters = {View.class}))
public @interface OnCustomEvent {
int[] value();
} Defensive patterns
Strategy: validation
Validate before calling
if (MyListener.class.getAnnotation(ListenerClass.class) == null) {
throw new IllegalStateException("MyListener is missing @ListenerClass");
} Prevention
- Treat @ListenerClass as mandatory metadata for any ButterKnife listener annotation
- Write one unit test per custom annotation asserting getAnnotation(ListenerClass.class) != null
When it happens
Trigger: Creating an annotation with a name or package that causes it to be routed to parseListenerAnnotation (e.g. registered via the processor's supported lists) without meta-annotating it with @ListenerClass; deleting the @ListenerClass line while refactoring a custom listener annotation.
Common situations: Writing a custom ButterKnife listener extension for the first time; copy-pasting an annotation class and stripping what looked like an unnecessary meta-annotation.
Related errors
- @%s's %s.%s missing @%s annotation.
- @%s annotation value() type not int[].
- No @%s defined on @%s's %s.%s.
- @%s annotation must be on a method.
- Multiple listener methods specified on @%s.
AI-assisted analysis of JakeWharton/butterknife@fcdebedf32 (2026-08-14).
Data as JSON: /api/errors/fd6a7e6f6f0944b4.
Report an issue: GitHub.