JakeWharton/butterknife · error · IllegalStateException
Both method() and callback() defined on @%s.
Error message
Both method() and callback() defined on @%s.
What it means
@ListenerClass declares both method() (a default single @ListenerMethod) and callbacks() (an enum of per-constant @ListenerMethod annotations). The processor requires that when method() has exactly one entry, callbacks() must remain the NONE sentinel; any other callbacks class while method() is populated throws this IllegalStateException at processing time. The two mechanisms are mutually exclusive.
Source
Thrown at butterknife-compiler/src/main/java/butterknife/compiler/ButterKnifeProcessor.java:1109
hasError = true;
}
} else {
error(element, "@%s annotation contains invalid ID %d. (%s.%s)",
annotationClass.getSimpleName(), id, enclosingElement.getQualifiedName(),
element.getSimpleName());
hasError = true;
}
}
}
ListenerMethod method;
ListenerMethod[] methods = listener.method();
if (methods.length > 1) {
throw new IllegalStateException(String.format("Multiple listener methods specified on @%s.",
annotationClass.getSimpleName()));
} else if (methods.length == 1) {
if (listener.callbacks() != ListenerClass.NONE.class) {
throw new IllegalStateException(
String.format("Both method() and callback() defined on @%s.",
annotationClass.getSimpleName()));
}
method = methods[0];
} else {
Method annotationCallback = annotationClass.getDeclaredMethod("callback");
Enum<?> callback = (Enum<?>) annotationCallback.invoke(annotation);
Field callbackField = callback.getDeclaringClass().getField(callback.name());
method = callbackField.getAnnotation(ListenerMethod.class);
if (method == null) {
throw new IllegalStateException(
String.format("No @%s defined on @%s's %s.%s.", ListenerMethod.class.getSimpleName(),
annotationClass.getSimpleName(), callback.getDeclaringClass().getSimpleName(),
callback.name()));
}
}
// Verify that the method has equal to or less than the number of parameters as the listener.View on GitHub (pinned to fcdebedf32)
Solutions
- Remove the method() attribute so the callbacks() enum drives binding
- Or reset callbacks to the default by deleting the callbacks attribute if the single method() entry is what you want
Example fix
// before
@ListenerClass(
method = @ListenerMethod(name = "onClick", parameters = {View.class}),
callbacks = Callback.class)
public @interface OnThing { ... }
// after
@ListenerClass(callbacks = Callback.class)
public @interface OnThing { ... } Defensive patterns
Strategy: validation
Validate before calling
ListenerClass lc = OnCustomEvent.class.getAnnotation(ListenerClass.class);
if (lc.method().length == 1 && lc.callbacks() != ListenerClass.NONE.class) {
throw new IllegalStateException("method() and callbacks() are mutually exclusive");
} Prevention
- Fill in either method() or callbacks() on @ListenerClass, never both
- Keep a single canonical template for custom listener annotations
When it happens
Trigger: Writing @ListenerClass(method = @ListenerMethod(...), callbacks = MyCallback.class) on a custom listener annotation — filling in both attributes.
Common situations: Starting from a method-based definition and later adding a callbacks enum (or vice versa) without clearing the other attribute; template code that pre-fills both.
Related errors
- Multiple listener methods specified on @%s.
- @%s's %s.%s missing @%s annotation.
- @%s annotation must be on a method.
- @%s annotation value() type not int[].
- No @%s defined on @%s's %s.%s.
AI-assisted analysis of JakeWharton/butterknife@fcdebedf32 (2026-08-14).
Data as JSON: /api/errors/253d150eab61df64.
Report an issue: GitHub.