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
- Check the cause chain: remove `final` from the annotated field and ensure it is not private/static (see validateMember rules).
- Add ProGuard keep rules for annotated fields and the target classes when using butterknife-reflect.
- Replace butterknife-reflect with butterknife-compiler so field assignment is direct code, not reflection.
- 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
- Never mark bound fields final/private/static.
- Keep ProGuard rules for annotated fields when using butterknife-reflect.
- Prefer butterknife-compiler: direct assignment, no reflection.
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
- Unable to invoke on with arguments
- Unable to invoke {}
- Unable to find binding constructor for {}
- must not be private.
- More than one bind annotation on
AI-assisted analysis of JakeWharton/butterknife@fcdebedf32 (2026-08-14).
Data as JSON: /api/errors/c06ac88c178107f9.
Report an issue: GitHub.