{"record":{"id":"ab17d534504b1a8e","repo":"airbnb/epoxy","slug":"checked-change-listener-cannot-be-null","errorCode":null,"errorMessage":"Checked change listener cannot be null","messagePattern":"Checked change listener cannot be null","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/WrappedEpoxyModelCheckedChangeListener.java","lineNumber":21,"sourceCode":"import android.widget.CompoundButton;\nimport android.widget.CompoundButton.OnCheckedChangeListener;\n\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * Used in the generated models to transform normal checked change listener to model\n * checked change.\n */\npublic class WrappedEpoxyModelCheckedChangeListener<T extends EpoxyModel<?>, V>\n    implements OnCheckedChangeListener {\n\n  private final OnModelCheckedChangeListener<T, V> originalCheckedChangeListener;\n\n  public WrappedEpoxyModelCheckedChangeListener(\n      OnModelCheckedChangeListener<T, V> checkedListener\n  ) {\n    if (checkedListener == null) {\n      throw new IllegalArgumentException(\"Checked change listener cannot be null\");\n    }\n\n    this.originalCheckedChangeListener = checkedListener;\n  }\n\n  @Override\n  public void onCheckedChanged(CompoundButton button, boolean isChecked) {\n    EpoxyViewHolder epoxyHolder = ListenersUtils.getEpoxyHolderForChildView(button);\n    if (epoxyHolder == null) {\n      // Initial binding can trigger the checked changed listener when the checked value is set.\n      // The view is not attached at this point so the holder can't be looked up, and in any case\n      // it is generally better to not trigger a callback for the binding anyway, since it isn't\n      // a user action.\n      //\n      // https://github.com/airbnb/epoxy/issues/797\n      return;\n    }\n","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/WrappedEpoxyModelCheckedChangeListener.java#L3-L39","documentation":"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.","triggerScenarios":"new WrappedEpoxyModelCheckedChangeListener<>(null), typically when a listener variable is null at wiring time.","commonSituations":"Passing a listener field not yet initialized; conditionally created listeners that end up null; Kotlin/Java interop passing a null from an optional callback.","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)"],"exampleFix":"// before\nmodel.onCheckedChangeListener(nullableListener); // wraps to IllegalArgument\n// after\nif (listener == null) listener = (model, view, isChecked) -> {};\nmodel.onCheckedChangeListener(listener);","handlingStrategy":"validation","validationCode":"requireNotNull(listener) { \"Checked change listener cannot be null\" }","typeGuard":"fun <T,V> validListener(l: OnModelCheckedChangeListener<T,V>?) = l ?: object : OnModelCheckedChangeListener<T,V> { override fun onCheckedChanged(m: T, v: V, isChecked: Boolean) {} }","tryCatchPattern":"try { model.onCheckedChangeListener(listener) } catch (e: IllegalArgumentException) { Log.w(TAG, \"null listener skipped\") }","preventionTips":["Initialize listeners before wiring models","Prefer no-op listeners over null","Use lateinit or non-null types in Kotlin"],"tags":["android","epoxy","null-argument","illegal-argument"],"backgroundTag":"null-argument","analyzedSha":"e45bd3a61fe3a1f130e184f5b8dcf172ab99025a","analyzedAt":"2026-09-13T03:24:16.052Z","contentChangedAt":"2026-09-13T03:24:16.052Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}