{"record":{"id":"7cbf0bf487881e1d","repo":"airbnb/epoxy","slug":"a-model-was-swiped-that-is-not-a-valid-target","errorCode":null,"errorMessage":"A model was swiped that is not a valid target: ","messagePattern":"A model was swiped that is not a valid target: ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModelTouchCallback.java","lineNumber":102,"sourceCode":"\n    //noinspection unchecked\n    onModelMoved(fromPosition, toPosition, (T) model, viewHolder.itemView);\n    return true;\n  }\n\n  @Override\n  public void onModelMoved(int fromPosition, int toPosition, T modelBeingMoved, View itemView) {\n\n  }\n\n  @Override\n  protected void onSwiped(EpoxyViewHolder viewHolder, int direction) {\n    EpoxyModel<?> model = viewHolder.getModel();\n    View view = viewHolder.itemView;\n    int position = viewHolder.getAdapterPosition();\n\n    if (!isTouchableModel(model)) {\n      throw new IllegalStateException(\n          \"A model was swiped that is not a valid target: \" + model.getClass());\n    }\n\n    //noinspection unchecked\n    onSwipeCompleted((T) model, view, position, direction);\n  }\n\n  @Override\n  public void onSwipeCompleted(T model, View itemView, int position, int direction) {\n\n  }\n\n  @Override\n  protected void onSelectedChanged(@Nullable EpoxyViewHolder viewHolder, int actionState) {\n    super.onSelectedChanged(viewHolder, actionState);\n\n    if (viewHolder != null) {\n      EpoxyModel<?> model = viewHolder.getModel();","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModelTouchCallback.java#L84-L120","documentation":"When a swipe gesture completes, onSwiped validates that the swiped model is one the callback can handle via isTouchableModel before invoking onSwipeCompleted. A swipe on any other model type throws IllegalStateException including the model's class, since the callback has no typed swipe handler for it.","triggerScenarios":"A swipe gesture lands on a model for which isTouchableModel(model) returns false, and onSwiped is invoked with that view holder.","commonSituations":"ItemTouchHelper swiping enabled for all view types while only some models are swipable; adding new model types to the adapter without updating the touch callback's isTouchableModel; swipe flags not filtered by position/type.","solutions":["Update isTouchableModel to accept the model type you want swipable, and implement onSwipeCompleted for it","Disable swiping for non-touchable models by overriding swipe-related enablement per view holder/position","Consume swipes in getSwipeDirs (return 0 for models that are not touchable)"],"exampleFix":"// before\n@Override public int getSwipeDirs(RecyclerView rv, RecyclerView.ViewHolder vh) { return ItemTouchHelper.LEFT; } // swipes everything\n// after\n@Override public int getSwipeDirs(RecyclerView rv, RecyclerView.ViewHolder vh) { return isTouchableModel(((EpoxyViewHolder) vh).getModel()) ? ItemTouchHelper.LEFT : 0; }","handlingStrategy":"type-guard","validationCode":"if (!touchCallback.isTouchableModel(viewHolder.getModel())) { return 0; } // no swipe dirs","typeGuard":"if (viewHolder.getModel() instanceof MySwipableModel_) { /* safe to swipe */ }","tryCatchPattern":"try { handleSwipe(model, direction); } catch (IllegalStateException e) { Log.e(TAG, \"Swiped non-touchable model\", e); }","preventionTips":["Return 0 swipe dirs for non-touchable models via getSwipeDirs","Update isTouchableModel when adding new model types","Test swipe gestures on every model type in the adapter"],"tags":["android","epoxy","swipe","type-mismatch"],"backgroundTag":"invalid-argument-value","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"}