JakeWharton/butterknife · error · IllegalStateException

@BindDrawable field type must be 'Drawable'. (

Error message

@BindDrawable field type must be 'Drawable'. (

What it means

Thrown by the reflection-based ButterKnife runtime when a field annotated with @BindDrawable is not declared as android.graphics.drawable.Drawable. The reflect binder resolves the annotation's resource (optionally tinted) and can only assign it to a Drawable-typed field; subtypes (e.g. a specific GradientDrawable) or other types are rejected because assignment is checked by exact class equality.

Source

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

  private static @Nullable Unbinder parseBindDrawable(Object target, Field field, View source) {
    BindDrawable bindDrawable = field.getAnnotation(BindDrawable.class);
    if (bindDrawable == null) {
      return null;
    }
    validateMember(field);

    int id = bindDrawable.value();
    int tint = bindDrawable.tint();
    Context context = source.getContext();

    Class<?> fieldType = field.getType();
    Object value;
    if (fieldType == Drawable.class) {
      value = tint != Constants.NO_RES_ID
          ? Utils.getTintedDrawable(context, id, tint)
          : ContextCompat.getDrawable(context, id);
    } else {
      throw new IllegalStateException("@BindDrawable field type must be 'Drawable'. ("
          + field.getDeclaringClass().getName()
          + '.'
          + field.getName()
          + ')');
    }
    trySet(field, target, value);

    return Unbinder.EMPTY;
  }

  private static @Nullable Unbinder parseBindFloat(Object target, Field field, View source) {
    BindFloat bindInt = field.getAnnotation(BindFloat.class);
    if (bindInt == null) {
      return null;
    }
    validateMember(field);

    int id = bindInt.value();

View on GitHub (pinned to fcdebedf32)

Solutions

  1. Declare the field as `Drawable` and cast to the concrete type after bind if a subclass API is needed.
  2. If a Bitmap was intended, load it with BitmapFactory/BitmapFactory-decodeResource instead of @BindDrawable.
  3. Prefer butterknife-compiler over butterknife-reflect in production builds so the error surfaces at compile time.

Example fix

// before
@BindDrawable(R.drawable.bg_card) GradientDrawable bg;

// after
@BindDrawable(R.drawable.bg_card) Drawable bg;
// then: GradientDrawable g = (GradientDrawable) bg;
Defensive patterns

Strategy: type-guard

Validate before calling

for (Field f : targetClass.getDeclaredFields()) {
  if (f.isAnnotationPresent(BindDrawable.class)
      && f.getType() != Drawable.class) {
    throw new IllegalStateException(f + " must be Drawable");
  }
}

Type guard

static boolean isValidBindDrawableField(Field f) {
  return !f.isAnnotationPresent(BindDrawable.class)
      || f.getType() == Drawable.class;
}

Prevention

When it happens

Trigger: Calling ButterKnife.bind(...) via butterknife-reflect on a class with a field such as `@BindDrawable(R.drawable.icon) GradientDrawable icon;` or `@BindDrawable(R.drawable.icon) Bitmap bitmap;` — anything where field.getType() != Drawable.class.

Common situations: Declaring the field as a concrete drawable subclass (VectorDrawable, GradientDrawable, BitmapDrawable) expecting polymorphic assignment, or annotating a Bitmap/ImageView field by mistake. The compiler path rejects this at build time; only the reflect path throws at runtime (typical in local JVM tests using butterknife-reflect).

Related errors


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