airbnb/epoxy · error · IllegalStateException
A model was selected that is not a valid target:
Error message
A model was selected that is not a valid target:
What it means
Epoxy throws this IllegalStateException from ItemTouchHelper's onSelectedChanged callback when a ViewHolder begins a drag or swipe but its model is not registered as touchable. EpoxyModelTouchCallback only permits models explicitly allowed by isTouchableModel, so an unregistered model being touched indicates a misconfiguration between the adapter's models and the touch callback.
Solutions
- Override isTouchableModel in your EpoxyModelTouchCallback subclass to return true for the model class being selected
- Ensure the touch callback is attached to a controller/adapter whose models all pass isTouchableModel
- Check that the model class being swiped/dragged matches the class registered in the callback (subclass/generics mismatch)
Example fix
// before
@Override
protected boolean isTouchableModel(EpoxyModel<?> model) {
return model instanceof SwipeOutModel;
}
// after
@Override
protected boolean isTouchableModel(EpoxyModel<?> model) {
return model instanceof SwipeOutModel || model instanceof DraggableModel;
} Defensive patterns
Strategy: validation
Validate before calling
if (callback.isTouchableModel(model)) {
itemTouchHelper.attachToRecyclerView(recyclerView);
} Type guard
boolean isTouchable(EpoxyModel<?> m) { return m instanceof SwipeOutModel || m instanceof DraggableModel; } Try / catch
try { touchHelper.onSwiped(vh, dir); } catch (IllegalStateException e) { Log.w(TAG, "non-touchable model selected", e); } Prevention
- Update isTouchableModel whenever adding a new model type to the list
- Only attach ItemTouchHelper to lists of touchable models
- Avoid swapping model data mid-gesture
When it happens
Trigger: A swipe/drag starts on a view holder whose model is not in the set of touchable models (e.g. the model list contains a model type not handled by isTouchableModel, or touchable model registration was changed/omitted).
Common situations: Adding new model types to a RecyclerView with swipe-to-dismiss or drag-and-drop without updating the touch callback's allowed models; mixing generic EpoxyModel instances into a list intended only for touchable models.
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
- 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/242ef9d452b39d2d.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModelTouchCallback.java:122
}
//noinspection unchecked
onSwipeCompleted((T) model, view, position, direction);
}
@Override
public void onSwipeCompleted(T model, View itemView, int position, int direction) {
}
@Override
protected void onSelectedChanged(@Nullable EpoxyViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if (viewHolder != null) {
EpoxyModel<?> model = viewHolder.getModel();
if (!isTouchableModel(model)) {
throw new IllegalStateException(
"A model was selected that is not a valid target: " + model.getClass());
}
markRecyclerViewHasSelection((RecyclerView) viewHolder.itemView.getParent());
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
holderBeingSwiped = viewHolder;
//noinspection unchecked
onSwipeStarted((T) model, viewHolder.itemView, viewHolder.getAdapterPosition());
} else if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
holderBeingDragged = viewHolder;
//noinspection unchecked
onDragStarted((T) model, viewHolder.itemView, viewHolder.getAdapterPosition());
}
} else if (holderBeingDragged != null) {
//noinspection unchecked
onDragReleased((T) holderBeingDragged.getModel(), holderBeingDragged.itemView);
holderBeingDragged = null;View on GitHub (pinned to e45bd3a61f)