airbnb/epoxy · error · IllegalEpoxyUsage
Cannot change a model's id after it has been added to the…
Error message
Cannot change a model's id after it has been added to the adapter.
What it means
Epoxy models get an id used by the adapter for diffing and stable item identity. Once the model has been added to an adapter (or its controller has been built), changing the id would silently break diffing, so Epoxy throws IllegalEpoxyUsage.
Solutions
- Create a new model instance instead of mutating the id of an already-added one
- Set the id once at model creation time, before the model reaches the controller/adapter
- Call controller.requestModelBuild() so buildModels recreates the model with the new id
- Only mutate models inside buildModels (or an interceptor before the adapter is set)
Example fix
// before
private final HeaderModel_ header = new HeaderModel_();
void update(long id) { header.id(id); } // throws after adapter set
// after
void update(long id) { requestModelBuild(); } // and in buildModels: new HeaderModel_().id(id) Defensive patterns
Strategy: validation
Validate before calling
if (model.addedToAdapter() /* or track via controller */) throw new IllegalStateException("Set id before adding to adapter"); model.id(newId); Try / catch
try { model.id(newId); } catch (IllegalEpoxyUsage e) { requestModelBuild(); } Prevention
- Set ids at model construction, never on cached instances
- Treat models as immutable once handed to buildModels
- Use requestModelBuild() for id changes
When it happens
Trigger: Calling model.id(long) (or id(long,...)) after the model was already added to an adapter or built by a controller, i.e. when addedToAdapter is true or firstControllerAddedTo is non-null and the new id differs from the current one.
Common situations: Reusing a model instance across buildModels passes and re-setting its id; mutating a retained model's id inside an interceptor after models were set on the adapter; caching a model in an Activity field and updating its id on new data.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 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/666b4776604a13da.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModel.java:238
@FloatRange(from = 0.0f, to = 100.0f) float percentVisibleWidth,
@Px int visibleHeight,
@Px int visibleWidth,
@NonNull T view
) {
}
public long id() {
return id;
}
/**
* Override the default id in cases where the data subject naturally has an id, like an object
* from a database. This id can only be set before the model is added to the adapter, it is an
* error to change the id after that.
*/
public EpoxyModel<T> id(long id) {
if ((addedToAdapter || firstControllerAddedTo != null) && id != this.id) {
throw new IllegalEpoxyUsage(
"Cannot change a model's id after it has been added to the adapter.");
}
hasDefaultId = false;
this.id = id;
return this;
}
/**
* Use multiple numbers as the id for this model. Useful when you don't have a single long that
* represents a unique id.
* <p>
* This hashes the numbers, so there is a tiny risk of collision with other ids.
*/
public EpoxyModel<T> id(@Nullable Number... ids) {
long result = 0;
if (ids != null) {
for (@Nullable Number id : ids) {View on GitHub (pinned to e45bd3a61f)