greenrobot/EventBus · error · EventBusException

${methodName} is a illegal @Subscribe method: must be public

Error message

${methodName} is a illegal @Subscribe method: must be public, non-static, and non-abstract

What it means

Thrown during method verification in SubscriberMethodFinder when strictMethodVerification is enabled and a method carries @Subscribe but its modifiers fail the check (modifiers & Modifier.PUBLIC) == 0 or (modifiers & MODIFIERS_IGNORE) != 0, i.e. the method is not public, or is static/abstract (MODIFIERS_IGNORE covers static, abstract, and synthetic/bridge). EventBus invokes handlers via Method.invoke on the public method; non-public, static, or abstract methods cannot serve as instance handlers.

Source

Thrown at EventBus/src/org/greenrobot/eventbus/SubscriberMethodFinder.java:191

                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                    throw new EventBusException("@Subscribe method " + methodName +
                            "must have exactly 1 parameter but has " + parameterTypes.length);
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
            }
        }
    }

    static void clearCaches() {
        METHOD_CACHE.clear();
    }

    static class FindState {
        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class<?> subscriberClass;
        Class<?> clazz;
        boolean skipSuperClasses;

View on GitHub (pinned to 0194926b3b)

Solutions

  1. Make the handler method public and non-static: public void onEvent(MessageEvent event)
  2. For abstract contracts, remove @Subscribe from the abstract method and annotate each concrete public override instead
  3. For static helpers, move the annotation to a public instance method that delegates

Example fix

// before
@Subscribe
protected void onMessage(MessageEvent event) { ... } // not public -> throws

// after
@Subscribe
public void onMessage(MessageEvent event) { ... }
Defensive patterns

Strategy: validation

Validate before calling

for (Method m : subscriberClass.getDeclaredMethods()) {
    Subscribe ann = m.getAnnotation(Subscribe.class);
    if (ann != null) {
        int mod = m.getModifiers();
        if (!Modifier.isPublic(mod) || Modifier.isStatic(mod) || Modifier.isAbstract(mod)) {
            throw new IllegalStateException("@Subscribe method " + m + " must be public, non-static, non-abstract");
        }
    }
}

Prevention

When it happens

Trigger: Annotating a private, protected, or package-private handler; annotating a static method (often a util 'handler'); annotating an abstract method in a base class that concrete subclasses implement; registering the class triggers the scan and throws.

Common situations: Kotlin functions default to public so this mostly hits Java code with visibility reductions after refactoring; abstract base-class handler contracts; IDE auto-generating static helper overloads; deliberate encapsulation (private handlers) by teams unaware EventBus requires public.

Related errors


AI-assisted analysis of greenrobot/EventBus@0194926b3b (2026-08-14). Data as JSON: /api/errors/ec8a01bf563adc8f. Report an issue: GitHub.