JakeWharton/butterknife · error · IllegalStateException
must not be private or static
Error message
must not be private or static
What it means
Thrown by ButterKnife's reflection binder (validateMember) when an annotated binding field or listener method is declared private or static. Reflection-based assignment and invocation require accessible, instance-level members, and ButterKnife's contract (shared with the annotation processor) forbids private/static bound members so generated and reflective paths behave identically.
Source
Thrown at butterknife-reflect/src/main/java/butterknife/ButterKnife.java:982
String who = "method '" + name + "'";
List<T> views = new ArrayList<>(ids.length);
for (int id : ids) {
if (isRequired) {
views.add((T) Utils.findRequiredViewAsType(source, id, who, cls));
} else {
T view = (T) Utils.findOptionalViewAsType(source, id, who, cls);
if (view != null) {
views.add(view);
}
}
}
return views;
}
private static <T extends AccessibleObject & Member> void validateMember(T object) {
int modifiers = object.getModifiers();
if ((modifiers & (PRIVATE | STATIC)) != 0) {
throw new IllegalStateException(object.getDeclaringClass().getName()
+ "."
+ object.getName()
+ " must not be private or static");
}
if ((modifiers & PUBLIC) == 0) {
object.setAccessible(true);
}
}
/** Returns true when the return value should be propagated. Use a default otherwise. */
private static boolean validateReturnType(Method method, Class<?> expected) {
Class<?> returnType = method.getReturnType();
if (returnType == void.class) {
return false;
}
if (returnType != expected) {
String expectedType = "'" + expected.getName() + "'";
if (expected != void.class) {View on GitHub (pinned to fcdebedf32)
Solutions
- Remove `private` and `static` from the annotated field or method (package-private or public is fine).
- For fields, use at least package-private visibility; for @OnClick/@On* methods make them non-static instance methods.
- Switch to butterknife-compiler to get this as a compile-time error.
Example fix
// before
private @BindView(R.id.title) TextView title;
static @OnClick(R.id.done) void onDone() { }
// after
@BindView(R.id.title) TextView title;
@OnClick(R.id.done) void onDone() { } Defensive patterns
Strategy: validation
Validate before calling
for (Field f : targetClass.getDeclaredFields()) {
if (isBound(f)) {
int m = f.getModifiers();
if ((m & (Modifier.PRIVATE | Modifier.STATIC)) != 0) {
throw new IllegalStateException(f + " must not be private or static");
}
}
} Prevention
- Keep bound fields and @On* methods package-private or public and instance-level.
- Avoid private visibility for ButterKnife members even in utility-style classes.
- Switch to butterknife-compiler — same rule enforced at compile time.
When it happens
Trigger: ButterKnife.bind(...) via butterknife-reflect on a class containing `private @BindView(R.id.title) TextView title;` or `static @OnClick(R.id.btn) void click()` — any bound member whose modifiers include PRIVATE or STATIC.
Common situations: Encapsulating-style developers mark fields private; refactoring moves an @OnClick handler into a static utility or companion method; Kotlin properties compiled to private backing fields with wrong visibility. The compiler path reports the same rule at build time, so this runtime form appears with butterknife-reflect (tests, libraries, Gradle experimental plugins).
Related errors
- must not be private.
- @BindDimen field type must be 'int' or 'float'. (
- @BindDrawable field type must be 'Drawable'. (
- @BindFloat field type must be 'float'. (
- @BindFont style must be NORMAL, BOLD, ITALIC, or BOLD_ITALIC
AI-assisted analysis of JakeWharton/butterknife@fcdebedf32 (2026-08-14).
Data as JSON: /api/errors/5af45ff157f13732.
Report an issue: GitHub.