microg/GmsCore · error · IllegalStateException
Task is not yet complete
Error message
Task is not yet complete
What it means
TaskImpl.getResult() returns the task's result only after completion; the check order is: not-completed, cancelled, failed, then success. If you call getResult() while the Task is still pending, it throws this IllegalStateException because there is no result yet — this is a misuse of the API rather than a task failure.
Source
Thrown at play-services-tasks/src/main/java/org/microg/gms/tasks/TaskImpl.java:129
@Override
public <TContinuationResult> Task<TContinuationResult> continueWithTask(Executor executor, Continuation<TResult, Task<TContinuationResult>> continuation) {
ContinuationWithExecutor<TResult, TContinuationResult> c = new ContinuationWithExecutor<>(executor, continuation);
enqueueOrInvoke(c);
return c.getTask();
}
@Override
public Exception getException() {
synchronized (lock) {
return exception;
}
}
@Override
public TResult getResult() {
synchronized (lock) {
if (!completed) throw new IllegalStateException("Task is not yet complete");
if (cancelled) throw new CancellationException("Task is canceled");
if (exception != null) throw new RuntimeExecutionException(exception);
return result;
}
}
@Override
public <X extends Throwable> TResult getResult(Class<X> exceptionType) throws X {
synchronized (lock) {
if (!completed) throw new IllegalStateException("Task is not yet complete");
if (cancelled) throw new CancellationException("Task is canceled");
if (exceptionType.isInstance(exception)) throw exceptionType.cast(exception);
if (exception != null) throw new RuntimeExecutionException(exception);
return result;
}
}
@OverrideView on GitHub (pinned to 157c9d86ac)
Solutions
- Use the completion value: on a background thread call TResult r = Tasks.await(task) and use its return value, not task.getResult()
- On the main thread, read the result inside addOnSuccessListener/addOnCompleteListener callbacks
- If you must call getResult(), guard it with if (task.isComplete() && task.isSuccessful()) first
Example fix
// before
Task<QuerySnapshot> task = db.collection("c").get();
QuerySnapshot snap = task.getResult(); // IllegalStateException: not yet complete
// after
Task<QuerySnapshot> task = db.collection("c").get();
// background thread:
QuerySnapshot snap = Tasks.await(task);
// or main thread:
task.addOnSuccessListener(s -> useResult(s)); Defensive patterns
Strategy: try-catch
Validate before calling
if (!task.isComplete()) {
// not ready - await it (background thread) or use a listener (main thread)
TResult r = Tasks.await(task); // off main thread only
} Type guard
static <T> boolean hasResult(Task<T> t) {
return t.isComplete() && t.isSuccessful();
} Try / catch
try {
T result = Tasks.await(task); // never call task.getResult() on a pending task
} catch (IllegalStateException e) {
// should not happen via await(); guard with isComplete() if calling getResult() directly
} catch (ExecutionException e) {
// task failure
} Prevention
- Never treat Task<T> as a synchronous value holder; always await or listen
- Use the value returned by Tasks.await(), not task.getResult(), after awaiting
- Read results only inside addOnSuccessListener/addOnCompleteListener on the main thread
- Gate direct getResult() calls behind isComplete() && isSuccessful()
When it happens
Trigger: Calling task.getResult() directly after starting an async operation (e.g. right after a Firestore get()/Firebase auth call) without first awaiting completion via Tasks.await(), addOnCompleteListener, or checking isComplete().
Common situations: Forgetting Tasks.await() on a background thread; calling getResult() inside code that assumed synchronous behavior after migrating from blocking APIs; reading the result in a listener registered before completion instead of inside the completion callback; ignoring the return value of Tasks.await and using the original task.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Task is canceled
- Only one extension per type may be added
- Cannot add twice the same OnSwitchChangeListener
- Cannot remove OnSwitchChangeListener
- DataSet has already been built.
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/448d3bdd2d8aba99.
Report an issue: GitHub.