airbnb/epoxy · critical · RuntimeException

Unable to get Epoxy helper class.

Error message

Unable to get Epoxy helper class.

What it means

When the reflective helper creation fails with InvocationTargetException whose cause is neither RuntimeException nor Error, the library wraps the cause in RuntimeException('Unable to get Epoxy helper class.'). This means the generated helper's constructor ran and threw a checked/other throwable.

Solutions

  1. Read the cause chain (RuntimeException.getCause()) to find the original throwable thrown inside the helper constructor.
  2. Fix the throwing initializer in the controller so construction cannot fail.
  3. Align epoxy-compiler, epoxy-adapter, and epoxy-processor versions.
  4. Clean/rebuild to regenerate helpers after moving or renaming controllers.

Example fix

// before
class MyController extends EpoxyController {
  private final Config cfg = loadConfigChecked(); // throws checked exception
}

// after
class MyController extends EpoxyController {
  private final Config cfg;
  MyController() { this.cfg = loadConfigSafely(); } // no throwing initializer
}
Defensive patterns

Strategy: try-catch

Validate before calling

// audit controller field initializers for code that can throw during construction

Try / catch

try { helper = ControllerHelperLookup.getHelperForController(controller); } catch (RuntimeException e) { Throwable cause = e.getCause(); log("Helper ctor threw", cause); }

Prevention

When it happens

Trigger: The generated _EpoxyHelper constructor for a controller throws a checked exception (e.g. Exception from an initializer block or field initialization in the controller/helper) that is not a RuntimeException or Error.

Common situations: Controller field initializers or constructor-time code throwing checked exceptions; classpath conflicts causing NoClassDefFoundError-like checked paths; mismatched epoxy-compiler and epoxy-adapter versions producing broken generated code.

Related errors


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

Appendix: source

Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/ControllerHelperLookup.java:40

    if (constructor == null) {
      return NO_OP_CONTROLLER_HELPER;
    }

    try {
      return (ControllerHelper) constructor.newInstance(controller);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Unable to invoke " + constructor, e);
    } catch (InstantiationException e) {
      throw new RuntimeException("Unable to invoke " + constructor, e);
    } catch (InvocationTargetException e) {
      Throwable cause = e.getCause();
      if (cause instanceof RuntimeException) {
        throw (RuntimeException) cause;
      }
      if (cause instanceof Error) {
        throw (Error) cause;
      }
      throw new RuntimeException("Unable to get Epoxy helper class.", cause);
    }
  }

  @Nullable
  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

View on GitHub (pinned to e45bd3a61f)