JakeWharton/butterknife · error · IllegalStateException

@BindString field type must be 'String'. (

Error message

@BindString field type must be 'String'. (

What it means

Thrown by the reflection-based ButterKnife runtime when a field annotated with @BindString is not declared as java.lang.String. The binder calls context.getString(id) and only assigns to a String field; CharSequence, Spanned, or int fields fail the exact type check.

Source

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

    return Unbinder.EMPTY;
  }

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

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

    Class<?> fieldType = field.getType();
    Object value;
    if (fieldType == String.class) {
      value = context.getString(id);
    } else {
      throw new IllegalStateException("@BindString field type must be 'String'. ("
          + field.getDeclaringClass().getName()
          + '.'
          + field.getName()
          + ')');
    }
    trySet(field, target, value);

    return Unbinder.EMPTY;
  }

  private static @Nullable Unbinder parseOnCheckedChanged(final Object target, final Method method,
      View source) {
    OnCheckedChanged onCheckedChanged = method.getAnnotation(OnCheckedChanged.class);
    if (onCheckedChanged == null) {
      return null;
    }
    validateMember(method);
    validateReturnType(method, void.class);

View on GitHub (pinned to fcdebedf32)

Solutions

  1. Declare the field as `String`.
  2. If CharSequence/Spanned is needed, bind a String field and wrap it (e.g. Html.fromHtml) afterwards.
  3. Switch to butterknife-compiler so the mismatch is caught at compile time.

Example fix

// before
@BindString(R.string.welcome) CharSequence welcome;

// after
@BindString(R.string.welcome) String welcome;
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

static boolean isValidBindStringField(Field f) {
  return !f.isAnnotationPresent(BindString.class) || f.getType() == String.class;
}

Prevention

When it happens

Trigger: ButterKnife.bind(...) via butterknife-reflect on a class with `@BindString(R.string.label) CharSequence label;` or `@BindString(R.string.label) int labelResId;`.

Common situations: Declaring the field as CharSequence to accept styled/Spanned strings from other code paths, or annotating a resource-id field instead of the text field. The compiler path fails the build for this; reflect defers it to runtime (frequent in unit-test setups using butterknife-reflect).

Related errors


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