airbnb/epoxy · error · IllegalEpoxyUsage

You must set an id on a model before adding it. Use the…

Error message

You must set an id on a model before adding it. Use the @AutoModel annotation if you want an id to be automatically generated for you.

What it means

EpoxyController.addInternal validates every model added during buildModels. A model whose id is still the default (unset) cannot participate in diffing/recycling, so IllegalEpoxyUsage is thrown, pointing you to set an id or use @AutoModel for generated ids.

Solutions

  1. Call .id(...) with a stable unique value on every model before addTo
  2. Use the @AutoModel annotation on a field and add via that field so an id is generated
  3. Verify the Epoxy annotation processor is configured (kapt/ksp) so @AutoModel works
  4. If id legitimately depends on content, derive it from the bound data (e.g. item.getId())

Example fix

// before
new HeaderModel_("Hello").addTo(this); // crash
// after
new HeaderModel_("Hello").id("header").addTo(this);
Defensive patterns

Strategy: validation

Validate before calling

if (model.hasDefaultId()) { model.id(computeId()); } model.addTo(controller);

Try / catch

try { model.addTo(controller); } catch (IllegalEpoxyUsage e) { model.id(uniqueId).addTo(controller); }

Prevention

When it happens

Trigger: Calling addTo/addInternal with a model on which .id(...) was never called; forgetting id on a dynamically created model (new FooModel_()); relying on @AutoModel without applying the annotation processor.

Common situations: Constructing a generated model class and adding it without chaining .id(); a refactor removing the .id() call; the Epoxy annotation processor not running so @AutoModel fields are not generated/assigned.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyController.java:502

   * EpoxyController#buildModels()}.
   */
  protected void add(@NonNull List<? extends EpoxyModel<?>> modelsToAdd) {
    modelsBeingBuilt.ensureCapacity(modelsBeingBuilt.size() + modelsToAdd.size());

    for (EpoxyModel<?> model : modelsToAdd) {
      add(model);
    }
  }

  /**
   * Method to actually add the model to the list being built. Should be called after all
   * validations are done.
   */
  void addInternal(EpoxyModel<?> modelToAdd) {
    assertIsBuildingModels();

    if (modelToAdd.hasDefaultId()) {
      throw new IllegalEpoxyUsage(
          "You must set an id on a model before adding it. Use the @AutoModel annotation if you "
              + "want an id to be automatically generated for you.");
    }

    if (!modelToAdd.isShown()) {
      throw new IllegalEpoxyUsage(
          "You cannot hide a model in an EpoxyController. Use `addIf` to conditionally add a "
              + "model instead.");
    }

    // The model being added may not have been staged if it wasn't mutated before it was added.
    // In that case we may have a previously staged model that still needs to be added.
    clearModelFromStaging(modelToAdd);
    modelToAdd.controllerToStageTo = null;
    modelsBeingBuilt.add(modelToAdd);
  }

  /**

View on GitHub (pinned to e45bd3a61f)