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

  1. Check task.isCanceled() before reading the result and branch on it
  2. Catch CancellationException separately from ExecutionException/RuntimeException and treat it as expected control flow
  3. 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

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


AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06). Data as JSON: /api/errors/d676749bd81decca. Report an issue: GitHub.