airbnb/epoxy · error · IllegalArgumentException
Checked change listener cannot be null
Error message
Checked change listener cannot be null
What it means
WrappedEpoxyModelCheckedChangeListener wraps a delegate OnModelCheckedChangeListener and refuses null in its constructor, throwing this IllegalArgumentException. A null listener would only cause an NPE later, so it fails fast.
Solutions
- Ensure the listener is created before wiring it to the model
- Guard against null at the call site or provide a no-op listener instead of null
- Make the listener non-null by construction (object expression or lambda)
Example fix
// before
model.onCheckedChangeListener(nullableListener); // wraps to IllegalArgument
// after
if (listener == null) listener = (model, view, isChecked) -> {};
model.onCheckedChangeListener(listener); Defensive patterns
Strategy: validation
Validate before calling
requireNotNull(listener) { "Checked change listener cannot be null" } Type guard
fun <T,V> validListener(l: OnModelCheckedChangeListener<T,V>?) = l ?: object : OnModelCheckedChangeListener<T,V> { override fun onCheckedChanged(m: T, v: V, isChecked: Boolean) {} } Try / catch
try { model.onCheckedChangeListener(listener) } catch (e: IllegalArgumentException) { Log.w(TAG, "null listener skipped") } Prevention
- Initialize listeners before wiring models
- Prefer no-op listeners over null
- Use lateinit or non-null types in Kotlin
When it happens
Trigger: new WrappedEpoxyModelCheckedChangeListener<>(null), typically when a listener variable is null at wiring time.
Common situations: Passing a listener field not yet initialized; conditionally created listeners that end up null; Kotlin/Java interop passing a null from an optional callback.
Related errors
- Must have stable ids when saving view holder state
- State cannot be restored once views have been bound. It…
- Tried to restore instance state, but onSaveInstanceState…
- numItemsToPrefetch must be greater than 0
- Unable to invoke
AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13).
Data as JSON: /api/errors/ab17d534504b1a8e.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/WrappedEpoxyModelCheckedChangeListener.java:21
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import androidx.recyclerview.widget.RecyclerView;
/**
* Used in the generated models to transform normal checked change listener to model
* checked change.
*/
public class WrappedEpoxyModelCheckedChangeListener<T extends EpoxyModel<?>, V>
implements OnCheckedChangeListener {
private final OnModelCheckedChangeListener<T, V> originalCheckedChangeListener;
public WrappedEpoxyModelCheckedChangeListener(
OnModelCheckedChangeListener<T, V> checkedListener
) {
if (checkedListener == null) {
throw new IllegalArgumentException("Checked change listener cannot be null");
}
this.originalCheckedChangeListener = checkedListener;
}
@Override
public void onCheckedChanged(CompoundButton button, boolean isChecked) {
EpoxyViewHolder epoxyHolder = ListenersUtils.getEpoxyHolderForChildView(button);
if (epoxyHolder == null) {
// Initial binding can trigger the checked changed listener when the checked value is set.
// The view is not attached at this point so the holder can't be looked up, and in any case
// it is generally better to not trigger a callback for the binding anyway, since it isn't
// a user action.
//
// https://github.com/airbnb/epoxy/issues/797
return;
}
View on GitHub (pinned to e45bd3a61f)