JakeWharton/butterknife · error · IllegalStateException

must have return type of

Error message

 must have return type of 

What it means

Thrown by ButterKnife's reflection binder when an @On* listener method declares a non-void return type that does not exactly match the expected type of the listener callback (e.g. boolean for OnTouchListener/OnLongClickListener/OnEditorActionListener). ButterKnife supports either void or the listener's own return type so return values propagate correctly; anything else is rejected.

Source

Thrown at butterknife-reflect/src/main/java/butterknife/ButterKnife.java:1003

          + " 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) {
        expectedType = "'void' or " + expectedType;
      }
      throw new IllegalStateException(method.getDeclaringClass().getName()
          + "."
          + method.getName()
          + " must have return type of "
          + expectedType);
    }
    return true;
  }

  private static boolean isRequired(Method method) {
    return method.getAnnotation(Optional.class) == null;
  }

  private static ArgumentTransformer createArgumentTransformer(Method method,
      Class<?>[] callbackParameterTypes) {
    Class<?>[] targetParameterTypes = method.getParameterTypes();

    int targetParameterLength = targetParameterTypes.length;
    if (targetParameterLength == 0) {

View on GitHub (pinned to fcdebedf32)

Solutions

  1. Make the listener method return `void` when the return value is unused (ButterKnife supplies the default).
  2. Or return exactly the listener callback's type: boolean for @OnLongClick/@OnTouch/@OnEditorActionListener, void for clicks/checked/text changes.
  3. Match the parameter list too, then switch to butterknife-compiler for compile-time validation.

Example fix

// before
@OnLongClick(R.id.item) String onLongClick() { return "ok"; }

// after
@OnLongClick(R.id.item) boolean onLongClick() { return true; }
Defensive patterns

Strategy: validation

Validate before calling

// Mirror of validateReturnType for an @OnLongClick method
Method m = ...;
Class<?> rt = m.getReturnType();
if (rt != void.class && rt != boolean.class) {
  throw new IllegalStateException(m + " must return void or boolean");
}

Prevention

When it happens

Trigger: ButterKnife.bind(...) via butterknife-reflect where a method annotated @OnClick/@OnTouch/etc. returns a type that is neither void nor the callback's return type — e.g. `@OnTouch(R.id.area) boolean onTouch() {...}` on a listener whose callback returns void, or an @OnLongClick method returning String.

Common situations: Copying a listener implementation with the wrong signature, changing a handler to return a status object, or an @OnEditorActionListener method returning void while the framework expects boolean (silent behavior change) or vice versa. Compiler binder checks the same rule at build time; reflect defers it to bind time.

Related errors


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