greenrobot/EventBus · error · EventBusException

Could not send handler message

Error message

Could not send handler message

What it means

Thrown from HandlerPoster.enqueue() (the Android main-thread poster used for ThreadMode.MAIN delivery) when sendMessage(obtainMessage()) returns false. Handler.sendMessage returns false only when the message was not accepted — practically, when the Handler's Looper has quit (isQuitting/quitted) so no message can be enqueued. HandlerPoster rides the main Looper; enqueue() runs synchronized on the poster and flips handlerActive=true before sending, so a failed send leaves the poster marked active with a dead queue — hence the hard crash rather than silent event loss.

Source

Thrown at eventbus-android/src/main/java/org/greenrobot/eventbus/HandlerPoster.java:44

    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;

View on GitHub (pinned to 0194926b3b)

Solutions

  1. Ensure EventBus instances and posts do not outlive the Looper they deliver on: stop/cancel background producers before quitting the Looper
  2. In Robolectric tests, use Shadows.shadowOf(Looper.getMainLooper()).idle() to drain pending MAIN posts instead of quitting the looper
  3. Unregister subscribers and stop posting during component teardown (onDestroy of the posting producer)
  4. If using a custom Looper-scoped EventBus, keep that Looper alive for the bus's lifetime

Example fix

// before (Robolectric test)
backgroundExecutor.submit(() -> EventBus.getDefault().post(new UiEvent()));
shadowOf(Looper.getMainLooper()).quit(); // MAIN poster cannot send -> throws

// after
backgroundExecutor.submit(() -> EventBus.getDefault().post(new UiEvent()));
shadowOf(Looper.getMainLooper()).idle(); // drain queue on a live looper
Defensive patterns

Strategy: validation

Validate before calling

// Before posting a MAIN-delivered event from a background thread at teardown time
if (isShuttingDown()) { // your app-level shutdown flag set in onDestroy/onTerminate
    return;
}
EventBus.getDefault().post(new UiEvent());

Try / catch

try {
    EventBus.getDefault().post(event); // may reach HandlerPoster.enqueue on a dying looper
} catch (EventBusException e) {
    if ("Could not send handler message".equals(e.getMessage())) {
        // looper quit during teardown: drop the event, app is going down anyway
        Log.w(TAG, "Main looper unavailable, discarding " + event.getClass().getSimpleName());
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: Posting an event with a MAIN-thread subscriber while the target Looper is shutting down: posting from a background thread during process teardown, or a HandlerPoster constructed with a Looper from a dying thread (e.g. a test or instrumentation thread whose Looper.quit() was called, or EventBus instance outliving an Android component's lifecycle in unit tests using LooperMode).

Common situations: Robolectric/JVM tests that pause or quit the main looper while async posts are in flight; posting from a thread during Application shutdown; custom EventBus instances created against a worker Looper that then quits; race where a post lands after the process has begun terminating.

Related errors


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