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

  1. Remove `private` and `static` from the annotated field or method (package-private or public is fine).
  2. For fields, use at least package-private visibility; for @OnClick/@On* methods make them non-static instance methods.
  3. 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

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


AI-assisted analysis of JakeWharton/butterknife@fcdebedf32 (2026-08-14). Data as JSON: /api/errors/5af45ff157f13732. Report an issue: GitHub.