airbnb/epoxy · error · IllegalArgumentException
Controller cannot be null
Error message
Controller cannot be null
What it means
addWithDebugValidation is the internal hook generated models call when debug validation (validateEpoxyModelUsage) is enabled. It requires the owning EpoxyController; a null controller means the model's usage cannot be validated, so Epoxy throws IllegalArgumentException immediately.
Solutions
- Always add models inside a controller's buildModels method so a valid controller is provided
- Ensure the controller reference passed to the model is non-null before adding
- Disable validateEpoxyModelUsage only if you truly use models without a controller
Example fix
// before
model.addWithDebugValidation(null);
// after
@Override public void buildModels(List<EpoxyModel<?>> models) { model.addWithDebugValidation(this); } Defensive patterns
Strategy: type-guard
Validate before calling
if (controller == null) { throw new IllegalStateException("Controller must be assigned before adding models"); } Type guard
if (!(model instanceof GeneratedModel<?> g) || g.controller() == null) { skipAdd(); } Try / catch
try { model.addTo(controller); } catch (IllegalArgumentException e) { Log.e(TAG, "Model added without controller", e); } Prevention
- Always add models inside buildModels
- Initialize controller fields before model setup
When it happens
Trigger: A generated model's addWithDebugValidation(controller) is invoked with a null controller argument, typically when the model's controller reference was never set or was passed as null from generated code.
Common situations: Manually invoking internal API; generated model used outside a controller's buildModels scope while validation is enabled; a controller field not yet assigned when the model is added.
Related errors
- You must set an id on a model before adding it. Use the…
- This model was already added to the controller at position
- Epoxy attribute fields on a model cannot be changed once…
- Models cannot be empty
- Must have stable ids when saving view holder state
AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13).
Data as JSON: /api/errors/d50f42d4be1b8d0f.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModel.java:414
*/
public void addIf(@NonNull AddPredicate predicate, @NonNull EpoxyController controller) {
addIf(predicate.addIf(), controller);
}
/**
* @see #addIf(AddPredicate, EpoxyController)
*/
public interface AddPredicate {
boolean addIf();
}
/**
* This is used internally by generated models to turn on validation checking when
* "validateEpoxyModelUsage" is enabled and the model is used with an {@link EpoxyController}.
*/
protected final void addWithDebugValidation(@NonNull EpoxyController controller) {
if (controller == null) {
throw new IllegalArgumentException("Controller cannot be null");
}
if (controller.isModelAddedMultipleTimes(this)) {
throw new IllegalEpoxyUsage(
"This model was already added to the controller at position "
+ controller.getFirstIndexOfModelInBuildingList(this));
}
if (firstControllerAddedTo == null) {
firstControllerAddedTo = controller;
// We save the current hashCode so we can compare it to the hashCode at later points in time
// in order to validate that it doesn't change and enforce mutability.
hashCodeWhenAdded = hashCode();
// The one time it is valid to change the model is during an interceptor callback. To support
// that we need to update the hashCode after interceptors have been run.
// The model can be added to multiple controllers, but we only allow an interceptor changeView on GitHub (pinned to e45bd3a61f)