{"record":{"id":"318a90af267e2604","repo":"python/cpython","slug":"task-does-not-support-set-exception-operation","errorCode":null,"errorMessage":"Task does not support set_exception operation","messagePattern":"Task does not support set_exception operation","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/tasks.py","lineNumber":147,"sourceCode":"        return base_tasks._task_repr(self)\n\n    def get_coro(self):\n        return self._coro\n\n    def get_context(self):\n        return self._context\n\n    def get_name(self):\n        return self._name\n\n    def set_name(self, value):\n        self._name = str(value)\n\n    def set_result(self, result):\n        raise RuntimeError('Task does not support set_result operation')\n\n    def set_exception(self, exception):\n        raise RuntimeError('Task does not support set_exception operation')\n\n    def get_stack(self, *, limit=None):\n        \"\"\"Return the list of stack frames for this task's coroutine.\n\n        If the coroutine is not done, this returns the stack where it is\n        suspended.  If the coroutine has completed successfully or was\n        cancelled, this returns an empty list.  If the coroutine was\n        terminated by an exception, this returns the list of traceback\n        frames.\n\n        The frames are always ordered from oldest to newest.\n\n        The optional limit gives the maximum number of frames to\n        return; by default all available frames are returned.  Its\n        meaning differs depending on whether a stack or a traceback is\n        returned: the newest frames of a stack are returned, but the\n        oldest frames of a traceback are returned.  (This matches the\n        behavior of the traceback module.)","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/tasks.py#L129-L165","documentation":"RuntimeError raised by Task.set_exception(): like set_result, it is permanently disabled on Task because a task's outcome comes only from running its coroutine. The method exists only because Task subclasses Future; invoking it is always a programming error.","triggerScenarios":"Calling task.set_exception(exc) directly; passing a Task where a manually-completed Future is expected in error-injection code, test doubles, or bridges that 'fail' a future (e.g. call_soon_threadsafe(fut.set_exception, err)) and receiving a Task.","commonSituations":"Error-injection test helpers built around Future APIs; thread-to-loop bridges that fail a pending wait on shutdown; shared 'waiter' abstractions that accept both tasks and futures via ensure_future and then try to fail them externally.","solutions":["Fail a loop.create_future() instead; the awaiting task should observe and re-raise it","Deliver errors to running tasks through cancellation (task.cancel()) or via a queue/future the task awaits","Type-check helper inputs: if futures.isfuture(x) and not isinstance(x, asyncio.Task) before calling set_exception"],"exampleFix":"// before\ndef abort_waiter(fut, err):\n    loop.call_soon_threadsafe(fut.set_exception, err)  # crashes if fut is a Task\n\n// after\nwaiter = loop.create_future()  # created where completion is controlled\nasync def consumer():\n    try:\n        return await waiter\n    except MyAbort:\n        return None","handlingStrategy":"validation","validationCode":"null","typeGuard":"import asyncio\n\ndef is_failable_future(f) -> bool:\n    return isinstance(f, asyncio.Future) and not isinstance(f, asyncio.Task)","tryCatchPattern":"null","preventionTips":["Fail waiters created with loop.create_future(); abort tasks with task.cancel()","In test error-injection helpers, assert the target is not a Task before set_exception","Keep waiter registries typed to Future, not Task"],"tags":["asyncio","tasks","futures","api-misuse"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}