microg/GmsCore · error · IllegalStateException
Must not be invoked on main thread
Error message
Must not be invoked on main thread
What it means
The unbounded Tasks.await(task) overload blocks the calling thread indefinitely, so it refuses to run on the Android main thread: it compares Looper.getMainLooper().getThread() to the current thread and throws IllegalStateException('Must not be invoked on main thread'). Blocking the main thread would freeze the UI and trigger ANRs.
Source
Thrown at play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java:70
if (task.isComplete()) return handleCompletedTask(task);
CountDownLatch latch = new CountDownLatch(1);
task.addOnCompleteListener(Runnable::run, completedTask -> latch.countDown());
if (latch.await(timeout, unit)) {
return handleCompletedTask(task);
}
throw new TimeoutException("Timed out waiting for Task");
}
/**
* Blocks until the specified Task is complete.
*
* @return the Task's result
* @throws ExecutionException if the Task fails
* @throws InterruptedException if an interrupt occurs while waiting for the Task to complete
*/
public static <TResult> TResult await(Task<TResult> task) throws ExecutionException, InterruptedException {
if (Looper.getMainLooper().getThread() == Thread.currentThread())
throw new IllegalStateException("Must not be invoked on main thread");
if (task == null) throw new IllegalArgumentException("Task must not be null");
if (task.isComplete()) return handleCompletedTask(task);
CountDownLatch latch = new CountDownLatch(1);
task.addOnCompleteListener(Runnable::run, completedTask -> latch.countDown());
latch.await();
return handleCompletedTask(task);
}
private static <TResult> TResult handleCompletedTask(Task<TResult> task) throws ExecutionException {
if (task.isSuccessful()) {
return task.getResult();
}
if (task.isCanceled()) {
throw new CancellationException("Task is already canceled");
}
throw new ExecutionException(task.getException());
}
View on GitHub (pinned to 157c9d86ac)
Solutions
- Move the await call to a background thread (worker thread, executor, or coroutine dispatch to IO)
- Use the listener-based APIs (addOnCompleteListener/addOnSuccessListener) instead of blocking await on the main thread
- Check Looper.myLooper() == Looper.getMainLooper() in your own wrapper and route to a background executor
- Use Tasks.await(task, timeout, unit) off-thread if you need a bounded wait
Example fix
// before // in onCreate() on main thread
User u = Tasks.await(getUserTask());
// after
executor.execute(() -> {
try {
User u = Tasks.await(getUserTask());
} catch (ExecutionException | InterruptedException e) {
// handle
}
}); Defensive patterns
Strategy: validation
Validate before calling
if (Looper.myLooper() == Looper.getMainLooper()) {
throw new IllegalStateException("await must run off the main thread");
} Try / catch
try {
T r = Tasks.await(task);
} catch (IllegalStateException e) {
if (e.getMessage().contains("main thread")) { /* re-dispatch to background executor */ }
} catch (ExecutionException | InterruptedException e) {
// normal failure handling
} Prevention
- Never call blocking await() from UI callbacks (onCreate, onClick, onResume)
- Route await through a dedicated background executor wrapper
- Prefer addOnCompleteListener on the main thread over blocking waits in UI code
- Add a debug-mode assertion that fails fast when awaiting on the main looper
When it happens
Trigger: Calling Tasks.await(task) from an Activity/Fragment method, a UI click handler, or any code executing on the main looper.
Common situations: Quick demo code written in onCreate; calling await from a ViewModel method invoked on the main thread; forgetting that callbacks like onClick run on the main thread.
Understand the failure class
Background: "unsupported platform" / "not supported on this platform" errors: what they mean and how to fix them — this error's family across 47 libraries.
Related errors
- Only one extension per type may be added
- Cannot add twice the same OnSwitchChangeListener
- Cannot remove OnSwitchChangeListener
- DataSet has already been built.
- Task is not yet completed
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/ab831037eb85727d.
Report an issue: GitHub.