airbnb/epoxy · critical · RuntimeException
Unable to find Epoxy Helper constructor for
Error message
Unable to find Epoxy Helper constructor for {clsName} What it means
findConstructorForClass loads <ClassName>_EpoxyHelper reflectively and gets its constructor taking the controller class. If the class exists but has no matching constructor (NoSuchMethodException), it throws RuntimeException('Unable to find Epoxy Helper constructor for <clsName>'). Note a missing class (ClassNotFoundException) instead walks up the superclass hierarchy.
Solutions
- Clean build to regenerate helper classes so signatures match controllers.
- Verify epoxy-compiler version matches epoxy-adapter across all modules.
- Rename or remove any hand-written class colliding with the <Controller>_EpoxyHelper name.
- Confirm the controller class wasn't renamed after generation; regenerate.
Example fix
// before // stale generated MyController_EpoxyHelper with no (MyController) ctor // after // clean rebuild: ./gradlew clean :app:build // regenerates MyController_EpoxyHelper(MyController)
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check generated class presence: Class.forName(clsName + "_EpoxyHelper").getConstructor(controllerClass);
Try / catch
try { ... } catch (RuntimeException e) { if (e.getMessage().startsWith("Unable to find Epoxy Helper constructor")) { /* regenerate code / fix build */ throw e; } } Prevention
- Run clean builds after renaming controllers.
- Avoid naming your own classes *_EpoxyHelper.
- Keep epoxy-compiler versions consistent across modules.
- Verify annotation processing is enabled in the IDE/build.
When it happens
Trigger: The generated <Class>_EpoxyHelper class exists but its constructor signature doesn't take the controller class — typically from stale generated code, a hand-written class name collision, or generated code from a different controller version.
Common situations: Partial incremental builds after renaming a controller; mixing epoxy-compiler versions across modules; a user class accidentally named <Controller>_EpoxyHelper shadowing generated code.
Related errors
- Unable to invoke
- Unable to get Epoxy helper class.
- Failed to hijack update handler in AsyncPagedListDiffer.You…
- Room couldn't find the constructor it is looking for in…
- Must have stable ids when saving view holder state
AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13).
Data as JSON: /api/errors/285a03ea6a2a7140.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/ControllerHelperLookup.java:63
private static Constructor<?> findConstructorForClass(Class<?> controllerClass) {
Constructor<?> helperCtor = BINDINGS.get(controllerClass);
if (helperCtor != null || BINDINGS.containsKey(controllerClass)) {
return helperCtor;
}
String clsName = controllerClass.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
return null;
}
try {
Class<?> bindingClass = Class.forName(clsName + GENERATED_HELPER_CLASS_SUFFIX);
//noinspection unchecked
helperCtor = bindingClass.getConstructor(controllerClass);
} catch (ClassNotFoundException e) {
helperCtor = findConstructorForClass(controllerClass.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find Epoxy Helper constructor for " + clsName, e);
}
BINDINGS.put(controllerClass, helperCtor);
return helperCtor;
}
}
View on GitHub (pinned to e45bd3a61f)