{"record":{"id":"d676749bd81decca","repo":"microg/GmsCore","slug":"task-is-canceled","errorCode":null,"errorMessage":"Task is canceled","messagePattern":"Task is canceled","errorType":"exception","errorClass":"CancellationException","httpStatus":null,"severity":"error","filePath":"play-services-tasks/src/main/java/org/microg/gms/tasks/TaskImpl.java","lineNumber":130,"sourceCode":"    @Override\n    public <TContinuationResult> Task<TContinuationResult> continueWithTask(Executor executor, Continuation<TResult, Task<TContinuationResult>> continuation) {\n        ContinuationWithExecutor<TResult, TContinuationResult> c = new ContinuationWithExecutor<>(executor, continuation);\n        enqueueOrInvoke(c);\n        return c.getTask();\n    }\n\n    @Override\n    public Exception getException() {\n        synchronized (lock) {\n            return exception;\n        }\n    }\n\n    @Override\n    public TResult getResult() {\n        synchronized (lock) {\n            if (!completed) throw new IllegalStateException(\"Task is not yet complete\");\n            if (cancelled) throw new CancellationException(\"Task is canceled\");\n            if (exception != null) throw new RuntimeExecutionException(exception);\n            return result;\n        }\n    }\n\n    @Override\n    public <X extends Throwable> TResult getResult(Class<X> exceptionType) throws X {\n        synchronized (lock) {\n            if (!completed) throw new IllegalStateException(\"Task is not yet complete\");\n            if (cancelled) throw new CancellationException(\"Task is canceled\");\n            if (exceptionType.isInstance(exception)) throw exceptionType.cast(exception);\n            if (exception != null) throw new RuntimeExecutionException(exception);\n            return result;\n        }\n    }\n\n    @Override\n    public boolean isCanceled() {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-tasks/src/main/java/org/microg/gms/tasks/TaskImpl.java#L112-L148","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nString value = Tasks.await(task).getResult(); // CancellationException if cancelled\n// after\nif (task.isComplete() && task.isCanceled()) {\n    // handle cancellation (retry / no-op)\n} else {\n    String value = Tasks.await(task);\n}","handlingStrategy":"validation","validationCode":"if (task.isComplete() && task.isCanceled()) {\n    // cancelled: no result exists, skip getResult entirely\n    return;\n}","typeGuard":"static <T> boolean isReadable(Task<T> t) {\n    return t.isComplete() && t.isSuccessful();\n}","tryCatchPattern":"try {\n    T result = Tasks.await(task);\n} catch (CancellationException e) {\n    // cancelled - retry or abort, not a failure\n} catch (ExecutionException e) {\n    // failed - inspect e.getCause()\n}","preventionTips":["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"],"tags":["android","cancellation","task-lifecycle","async"],"backgroundTag":"task-already-canceled","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}