microg/GmsCore · error · CancellationException
Task is already canceled
Error message
Task is already canceled
What it means
Tasks.await() calls handleCompletedTask() once the task has completed. If the completed Task was cancelled rather than successful or failed, the method throws a CancellationException with this message, since there is no result to return and no cause exception to wrap. It distinguishes an intentionally cancelled Task from a failed one.
Source
Thrown at play-services-tasks/src/main/java/com/google/android/gms/tasks/Tasks.java:84
* @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());
}
/**
* Returns a {@link Task} that will be completed with the result of the specified {@code Callable}.
* <p/>
* If a non-{@link Exception} throwable is thrown in the callable, the {@link Task} will be failed with a
* {@link RuntimeException} whose cause is the original throwable.
* <p/>
* The {@code Callable} will be called on the main application thread.
*
* @deprecated Use {@link TaskCompletionSource} instead, which allows the caller to manage their own Executor.
*/
@Deprecated
public static <TResult> Task<TResult> call(Callable<TResult> callable) {
return call(TaskExecutors.MAIN_THREAD, callable);
}View on GitHub (pinned to 157c9d86ac)
Solutions
- Check task.isCanceled() before calling await/getResult and handle the cancellation path explicitly
- Catch CancellationException separately from ExecutionException and treat it as a normal control-flow event (retry or abort)
- If cancellation is unexpected, audit the CancellationToken and all trySetCancelled/cancel call sites to find who cancelled the task
Example fix
// before
try {
Result r = Tasks.await(task);
} catch (ExecutionException e) {
// only handles failure
}
// after
try {
if (task.isCanceled()) {
// handle cancellation without throwing
return null;
}
Result r = Tasks.await(task);
} catch (CancellationException e) {
// task was cancelled - retry or propagate
} catch (ExecutionException e) {
// actual failure
} Defensive patterns
Strategy: try-catch
Validate before calling
if (task.isComplete() && task.isCanceled()) {
// handle cancellation before awaiting
return null; // or retry
} Type guard
static <T> boolean isAwaitable(Task<T> t) {
return t != null && !(t.isComplete() && t.isCanceled());
} Try / catch
try {
T result = Tasks.await(task);
} catch (CancellationException e) {
// cancelled: retry or abort gracefully
} catch (ExecutionException e) {
// actual failure: inspect e.getCause()
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} Prevention
- Always catch CancellationException separately from ExecutionException
- Audit CancellationToken and trySetCancelled call sites when cancellation surprises you
- Remember Tasks.withTimeout cancels its wrapper task on expiry - await() will surface this error
- Treat cancellation as control flow, not as a crash
When it happens
Trigger: Awaiting a Task whose TaskCompletionSource.trySetCancelled()/cancel() was called, or a Task cancelled by a CancellationToken (e.g. by Tasks.withTimeout when its timeout fires, or by an operation cancelled on logout/navigation), then calling getResult()/await() on it.
Common situations: A withTimeout wrapper fires while awaiting and the resulting task is cancelled; user navigates away and the app cancels pending Firestore/Firebase work; a CancellationToken passed to TaskCompletionSource is cancelled before completion; retry logic that cancels superseded requests.
Related errors
- Task is canceled
- Task $this was cancelled normally.
- continueWithTask is not implemented
- onSuccessTask is not implemented
- Must not be invoked on main thread
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/59498c1a22b0600c.
Report an issue: GitHub.