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
- Declare the field as `Drawable` and cast to the concrete type after bind if a subclass API is needed.
- If a Bitmap was intended, load it with BitmapFactory/BitmapFactory-decodeResource instead of @BindDrawable.
- 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
- Declare @BindDrawable fields exactly as android.graphics.drawable.Drawable.
- Cast to a concrete drawable subclass after bind if subclass APIs are needed.
- Use butterknife-compiler for compile-time validation in production builds.
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
- @BindDimen field type must be 'int' or 'float'. (
- @BindFloat field type must be 'float'. (
- @BindFont field type must be 'Typeface'. (
- @BindInt field type must be 'int'. (
- @BindString field type must be 'String'. (
AI-assisted analysis of JakeWharton/butterknife@fcdebedf32 (2026-08-14).
Data as JSON: /api/errors/6f90cd15e4fc0055.
Report an issue: GitHub.