airbnb/epoxy · error · IllegalStateException

A model was dragged that is not a valid target:

Error message

A model was dragged that is not a valid target: 

What it means

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.

Solutions

  1. Restrict dragging to valid models by overriding isDragEnabled(EpoxyViewHolder, int) to return true only for touchable models
  2. Update isTouchableModel to cover all model types that can actually be dragged
  3. Use a narrower ItemTouchHelper setup (e.g. no long-press drag) so only intended models can be dragged

Example fix

// before
@Override public boolean isDragEnabled(EpoxyViewHolder vh, int pos) { return true; } // any model draggable
// after
@Override public boolean isDragEnabled(EpoxyViewHolder vh, int pos) { return isTouchableModel(vh.getModel()); }
Defensive patterns

Strategy: type-guard

Validate before calling

if (!touchCallback.isTouchableModel(viewHolder.getModel())) { return false; } // don't start drag

Type guard

if (viewHolder.getModel() instanceof MyTouchableModel_) { /* safe to drag */ }

Try / catch

try { processMove(from, to); } catch (IllegalStateException e) { Log.e(TAG, "Dragged non-touchable model", e); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13). Data as JSON: /api/errors/bd907b5da17cbc59. Report an issue: GitHub.

Appendix: source

Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModelTouchCallback.java:81

    return targetModelClass.isInstance(model);
  }

  @Override
  protected boolean onMove(RecyclerView recyclerView, EpoxyViewHolder viewHolder,
      EpoxyViewHolder target) {

    if (controller == null) {
      throw new IllegalStateException(
          "A controller must be provided in the constructor if dragging is enabled");
    }

    int fromPosition = viewHolder.getAdapterPosition();
    int toPosition = target.getAdapterPosition();
    controller.moveModel(fromPosition, toPosition);

    EpoxyModel<?> model = viewHolder.getModel();
    if (!isTouchableModel(model)) {
      throw new IllegalStateException(
          "A model was dragged that is not a valid target: " + model.getClass());
    }

    //noinspection unchecked
    onModelMoved(fromPosition, toPosition, (T) model, viewHolder.itemView);
    return true;
  }

  @Override
  public void onModelMoved(int fromPosition, int toPosition, T modelBeingMoved, View itemView) {

  }

  @Override
  protected void onSwiped(EpoxyViewHolder viewHolder, int direction) {
    EpoxyModel<?> model = viewHolder.getModel();
    View view = viewHolder.itemView;
    int position = viewHolder.getAdapterPosition();

View on GitHub (pinned to e45bd3a61f)