microg/GmsCore · error · CancellationException
Task is canceled
Error message
Task is canceled
What it means
TaskImpl.getResult() throws a CancellationException with this message when the Task completed by cancellation. A cancelled Task has no result and no failure cause, so getResult() cannot return anything meaningful — callers must handle cancellation as its own outcome before reading the result.
Source
Thrown at play-services-tasks/src/main/java/org/microg/gms/tasks/TaskImpl.java:130
@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;
}
}
@Override
public boolean isCanceled() {View on GitHub (pinned to 157c9d86ac)
Solutions
- Check task.isCanceled() before reading the result and branch on it
- Catch CancellationException separately from ExecutionException/RuntimeException and treat it as expected control flow
- If cancellation is unexpected, find and remove the cancel source (CancellationToken, trySetCancelled call, or the withTimeout wrapper)
Example fix
// before
String value = Tasks.await(task).getResult(); // CancellationException if cancelled
// after
if (task.isComplete() && task.isCanceled()) {
// handle cancellation (retry / no-op)
} else {
String value = Tasks.await(task);
} Defensive patterns
Strategy: validation
Validate before calling
if (task.isComplete() && task.isCanceled()) {
// cancelled: no result exists, skip getResult entirely
return;
} Type guard
static <T> boolean isReadable(Task<T> t) {
return t.isComplete() && t.isSuccessful();
} Try / catch
try {
T result = Tasks.await(task);
} catch (CancellationException e) {
// cancelled - retry or abort, not a failure
} catch (ExecutionException e) {
// failed - inspect e.getCause()
} Prevention
- Check isCanceled() before reading results whenever tasks may be cancelled
- Handle Tasks.withTimeout expiry: its wrapper completes as cancelled on timeout
- Cancel-and-forget patterns should not share task references with result readers
- Always separate CancellationException handling from failure handling
When it happens
Trigger: Calling getResult() (directly or after Tasks.await() on an already-cancelled task) when the task was cancelled via TaskCompletionSource.setCancelled/trySetCancelled, a CancellationToken, or a Tasks.withTimeout expiry.
Common situations: Timeout wrapper fired before completion; user cancelled an operation (logout, screen dismissed) and pending tasks were cancelled; retry logic cancelling superseded requests while another thread still reads the result; awaiting a stale task after configuration change.
Related errors
- Task is already canceled
- Task is not yet complete
- No result
- Task $this was cancelled normally.
- deleteAll was set to true but keys were also provided
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/d676749bd81decca.
Report an issue: GitHub.