JakeWharton/butterknife · error · RuntimeException

Unable to assign to on

Error message

Unable to assign  to  on 

What it means

Wrapped in a RuntimeException and rethrown by ButterKnife's reflect binder (trySet) when Field.set fails with IllegalAccessException while assigning a bound resource/view value to an annotated field. The message names the value, field, and target; the cause carries the original exception.

Source

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

      @Override public String toString() {
        StringBuilder builder = new StringBuilder("ArgumentTransformer[");
        for (int i = 0; i < indexMap.length; i++) {
          if (i > 0) {
            builder.append(", ");
          }
          builder.append(i).append(" => ").append(indexMap[i]);
        }
        return builder.append(']').toString();
      }
    };
  }

  static void trySet(Field field, Object target, @Nullable Object value) {
    try {
      field.set(target, value);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Unable to assign " + value + " to " + field + " on " + target, e);
    }
  }

  private static Object tryInvoke(Method method, Object target, Object... arguments) {
    Throwable cause;
    try {
      return method.invoke(target, arguments);
    } catch (IllegalAccessException e) {
      cause = e;
    } catch (InvocationTargetException e) {
      cause = e;
    }
    throw new RuntimeException(
        "Unable to invoke " + method + " on " + target + " with arguments "
            + Arrays.toString(arguments), cause);
  }

  private static final Setter<CompoundButton, CompoundButton.OnCheckedChangeListener>

View on GitHub (pinned to fcdebedf32)

Solutions

  1. Check the cause chain: remove `final` from the annotated field and ensure it is not private/static (see validateMember rules).
  2. Add ProGuard keep rules for annotated fields and the target classes when using butterknife-reflect.
  3. Replace butterknife-reflect with butterknife-compiler so field assignment is direct code, not reflection.
  4. If running plain JVM tests without android.jar implementations, use Robolectric or an instrumentation test.

Example fix

// before
final @BindView(R.id.title) TextView title; // reflective write may fail

// after
@BindView(R.id.title) TextView title;
Defensive patterns

Strategy: try-catch

Validate before calling

int m = field.getModifiers();
if (Modifier.isFinal(m) || Modifier.isPrivate(m) || Modifier.isStatic(m)) {
  throw new IllegalStateException(field + " cannot be reflectively assigned");
}

Try / catch

try {
  ButterKnife.bind(target, source);
} catch (RuntimeException e) {
  if (e.getCause() instanceof IllegalAccessException) {
    // fix field modifiers / keep rules, then report
    throw new IllegalStateException("Binding blocked: " + e.getCause(), e);
  }
  throw e;
}

Prevention

When it happens

Trigger: ButterKnife.bind(...) via butterknife-reflect when field.set throws IllegalAccessException — typically a final field, a field in a module/SDK class where accessibility was suppressed, or a race where another component (e.g. an obfuscator or security manager) blocks reflective writes even after setAccessible(true).

Common situations: ProGuard/R8 stripping or renaming annotated fields while keeping the @BindView metadata; binding into classes loaded by a different classloader in plugin architectures; JVM unit tests (not Robolectric) where android.jar stubs throw; fields made final by a code generator. The compiler-generated binder does not use reflection, so this is specific to butterknife-reflect.

Related errors


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