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
- Ensure EventBus instances and posts do not outlive the Looper they deliver on: stop/cancel background producers before quitting the Looper
- In Robolectric tests, use Shadows.shadowOf(Looper.getMainLooper()).idle() to drain pending MAIN posts instead of quitting the looper
- Unregister subscribers and stop posting during component teardown (onDestroy of the posting producer)
- 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
- Stop background producers (threads, executors) before quitting loopers/process teardown
- In Robolectric, drain with shadowOf(Looper.getMainLooper()).idle() instead of quit()
- Never construct EventBus/HandlerPoster against a Looper that has a shorter lifetime than the bus
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
- It looks like you are using EventBus on Android, make sure t
- Subscriber ${subscriber.getClass()} already registered to ev
- Unexpected exception
- Could not find subscriber method in ${subscriberClass}. Mayb
- This method may only be called from inside event handling me
AI-assisted analysis of greenrobot/EventBus@0194926b3b (2026-08-14).
Data as JSON: /api/errors/e60c0ca19c38879f.
Report an issue: GitHub.