JakeWharton/butterknife · error · IllegalStateException
Parameter #{} of method '{}' was of the wrong type for param
Error message
Parameter #{} of method '{}' was of the wrong type for parameter #{} of method '{}'. See cause for more info. What it means
Thrown by Utils.castParam during listener-method argument wiring when a parameter of an @On* method cannot be cast to the parameter type the framework callback actually supplies. The original ClassCastException is the cause; the message names both methods (from/to) and both parameter positions so the mismatch is pinpointed.
Source
Thrown at butterknife-runtime/src/main/java/butterknife/internal/Utils.java:120
return cls.cast(view);
} catch (ClassCastException e) {
String name = getResourceEntryName(view, id);
throw new IllegalStateException("View '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was of the wrong type. See cause for more info.", e);
}
}
public static <T> T castParam(Object value, String from, int fromPos, String to, int toPos,
Class<T> cls) {
try {
return cls.cast(value);
} catch (ClassCastException e) {
throw new IllegalStateException("Parameter #"
+ (fromPos + 1)
+ " of method '"
+ from
+ "' was of the wrong type for parameter #"
+ (toPos + 1)
+ " of method '"
+ to
+ "'. See cause for more info.", e);
}
}
private static String getResourceEntryName(View view, @IdRes int id) {
if (view.isInEditMode()) {
return "<unavailable while editing>";
}
return view.getContext().getResources().getResourceEntryName(id);
}
View on GitHub (pinned to fcdebedf32)
Solutions
- Match the framework callback's parameter types exactly or accept a supertype (CharSequence instead of String, Object instead of a model class).
- Cast inside the method body (`((String) s)`) only when you control the producer and know the runtime type.
- Copy signatures from the wrapped listener interface docs (TextWatcher, AdapterView.OnItemClickListener, etc.).
Example fix
// before
@OnTextChanged(R.id.search) void onSearch(String text) { }
// after
@OnTextChanged(R.id.search) void onSearch(CharSequence text) { } Defensive patterns
Strategy: type-guard
Validate before calling
Class<?>[] cb = {CharSequence.class, int.class, int.class, int.class};
for (Class<?> p : method.getParameterTypes()) {
boolean ok = false;
for (Class<?> c : cb) ok |= c.isAssignableFrom(p);
if (!ok) throw new IllegalStateException(p + " cannot receive any callback param");
} Type guard
static boolean canReceive(Class<?> callbackParam, Class<?> methodParam) {
return callbackParam.isAssignableFrom(methodParam) || methodParam == callbackParam;
} Prevention
- Write listener signatures from the framework interface, from memory as 'widest type wins' (CharSequence, Object).
- Cast to the concrete type inside the method only when the producer is under your control.
- Use butterknife-compiler to catch parameter mismatches at build time.
When it happens
Trigger: With the reflect binder's argument matching or generated code, a listener parameter whose declared type is narrower than the callback's: e.g. `@OnTextChanged(R.id.q) void onText(String s)` (callback passes CharSequence), or `@OnItemClick void pick(String item)` where the adapter supplies Object — ClassCastException at castParam during bind/registration.
Common situations: Writing listener signatures from memory instead of the framework interface (String vs CharSequence is the classic case); custom adapters whose getItem returns Object; AdapterView callbacks delivering a generic type after a library update changed the callback signature.
Related errors
- Unable to match <class>.<method> method arguments.\n\n Para
- must have at most parameter(s).
- @%s's %s.%s missing @%s annotation.
- @%s annotation must be on a method.
- Multiple listener methods specified on @%s.
AI-assisted analysis of JakeWharton/butterknife@fcdebedf32 (2026-08-14).
Data as JSON: /api/errors/6d3ac81b399f5a0f.
Report an issue: GitHub.