airbnb/epoxy · error · UnsupportedOperationException

You should set a layout with layout(...) instead of using…

Error message

You should set a layout with layout(...) instead of using this.

What it means

Unlike most models, EpoxyModelGroup has no default layout — a layout must be supplied explicitly, since the group's purpose is to bind multiple models into a given layout. getDefaultLayout is therefore final and always throws UnsupportedOperationException to enforce this at construction time.

Solutions

  1. Always pass a layout resource to the group (e.g. EpoxyModelGroup_(R.layout.group_layout) or layout(R.layout.group_layout))
  2. Set the layout before the model is added in buildModels
  3. Do not subclass EpoxyModelGroup expecting to override getDefaultLayout — it is final

Example fix

// before
new EpoxyModelGroup_().models(models); // no layout -> throws
// after
new EpoxyModelGroup_(R.layout.my_group_layout).models(models).id(groupId).addTo(this);
Defensive patterns

Strategy: validation

Validate before calling

if (layoutRes == 0) throw new IllegalStateException("EpoxyModelGroup requires an explicit layout");

Try / catch

try { buildGroup(); } catch (UnsupportedOperationException e) { Log.e(TAG, "Group built without layout", e); }

Prevention

When it happens

Trigger: Constructing or building an EpoxyModelGroup without calling layout(layoutRes), so Epoxy falls back to getDefaultLayout during view type resolution.

Common situations: Forgetting the layout parameter in the group builder; migrating code from a normal model (which has a default layout) to a group; programmatically constructing EpoxyModelGroup with a missing layout argument path.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModelGroup.java:243

    for (int i = 0; i < modelCount; i++) {
      callback.onModel(models.get(i), holder.getViewHolders().get(i), i);
    }
  }

  private interface IterateModelsCallback {
    void onModel(EpoxyModel model, EpoxyViewHolder viewHolder, int modelIndex);
  }

  @Override
  public int getSpanSize(int totalSpanCount, int position, int itemCount) {
    // Defaults to using the span size of the first model. Override this if you need to customize it
    return models.get(0).spanSize(totalSpanCount, position, itemCount);
  }

  @Override
  protected final int getDefaultLayout() {
    throw new UnsupportedOperationException(
        "You should set a layout with layout(...) instead of using this.");
  }

  @NonNull
  public EpoxyModelGroup shouldSaveViewState(boolean shouldSaveViewState) {
    onMutation();
    this.shouldSaveViewState = shouldSaveViewState;
    return this;
  }

  @Override
  public boolean shouldSaveViewState() {
    // By default state is saved if any of the models have saved state enabled.
    // Override this if you need custom behavior.
    if (shouldSaveViewState != null) {
      return shouldSaveViewState;
    } else {
      return shouldSaveViewStateDefault;

View on GitHub (pinned to e45bd3a61f)