{"record":{"id":"bd907b5da17cbc59","repo":"airbnb/epoxy","slug":"a-model-was-dragged-that-is-not-a-valid-target","errorCode":null,"errorMessage":"A model was dragged that is not a valid target: ","messagePattern":"A model was dragged 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":81,"sourceCode":"    return targetModelClass.isInstance(model);\n  }\n\n  @Override\n  protected boolean onMove(RecyclerView recyclerView, EpoxyViewHolder viewHolder,\n      EpoxyViewHolder target) {\n\n    if (controller == null) {\n      throw new IllegalStateException(\n          \"A controller must be provided in the constructor if dragging is enabled\");\n    }\n\n    int fromPosition = viewHolder.getAdapterPosition();\n    int toPosition = target.getAdapterPosition();\n    controller.moveModel(fromPosition, toPosition);\n\n    EpoxyModel<?> model = viewHolder.getModel();\n    if (!isTouchableModel(model)) {\n      throw new IllegalStateException(\n          \"A model was dragged that is not a valid target: \" + model.getClass());\n    }\n\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();","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModelTouchCallback.java#L63-L99","documentation":"After a drag move completes, EpoxyModelTouchCallback verifies the dragged view holder's model is one of the touchable model types the callback accepts (isTouchableModel). If not, it throws IllegalStateException with the model's class so you know your drag handling touched a model it cannot handle.","triggerScenarios":"A drag is initiated on a model for which isTouchableModel(model) returns false — e.g. dragging a header or any non-T model that ItemTouchHelper still considers movable.","commonSituations":"isDragEnabled/isMovementWide flags allowing drags on all items while isTouchableModel only accepts a subset; adapters where multiple model types exist but drag setup doesn't filter by type; long-press default drag enabled on every row.","solutions":["Restrict dragging to valid models by overriding isDragEnabled(EpoxyViewHolder, int) to return true only for touchable models","Update isTouchableModel to cover all model types that can actually be dragged","Use a narrower ItemTouchHelper setup (e.g. no long-press drag) so only intended models can be dragged"],"exampleFix":"// before\n@Override public boolean isDragEnabled(EpoxyViewHolder vh, int pos) { return true; } // any model draggable\n// after\n@Override public boolean isDragEnabled(EpoxyViewHolder vh, int pos) { return isTouchableModel(vh.getModel()); }","handlingStrategy":"type-guard","validationCode":"if (!touchCallback.isTouchableModel(viewHolder.getModel())) { return false; } // don't start drag","typeGuard":"if (viewHolder.getModel() instanceof MyTouchableModel_) { /* safe to drag */ }","tryCatchPattern":"try { processMove(from, to); } catch (IllegalStateException e) { Log.e(TAG, \"Dragged non-touchable model\", e); }","preventionTips":["Gate isDragEnabled per model type","Keep isTouchableModel in sync with all draggable models","Avoid global long-press drag enablement"],"tags":["android","epoxy","drag-and-drop","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"}