{"record":{"id":"1b7ae5c7fd22d314","repo":"python/cpython","slug":"cannot-notify-on-un-acquired-lock","errorCode":null,"errorMessage":"cannot notify on un-acquired lock","messagePattern":"cannot notify on un-acquired lock","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/locks.py","lineNumber":327,"sourceCode":"        while not result:\n            await self.wait()\n            result = predicate()\n        return result\n\n    def notify(self, n=1):\n        \"\"\"By default, wake up one task waiting on this condition, if any.\n        If the calling task has not acquired the lock when this method\n        is called, a RuntimeError is raised.\n\n        This method wakes up n of the tasks waiting for the condition\n         variable; if fewer than n are waiting, they are all awoken.\n\n        Note: an awakened task does not actually return from its\n        wait() call until it can reacquire the lock. Since notify() does\n        not release the lock, its caller should.\n        \"\"\"\n        if not self.locked():\n            raise RuntimeError('cannot notify on un-acquired lock')\n        self._notify(n)\n\n    def _notify(self, n):\n        idx = 0\n        for fut in self._waiters:\n            if idx >= n:\n                break\n\n            if not fut.done():\n                idx += 1\n                fut.set_result(False)\n\n    def notify_all(self):\n        \"\"\"Wake up all tasks waiting on this condition. This method acts\n        like notify(), but wakes up all waiting tasks instead of one. If the\n        calling task has not acquired the lock when this method is called,\n        a RuntimeError is raised.\n        \"\"\"","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/locks.py#L309-L345","documentation":"asyncio.Condition.notify()/notify_all() raise RuntimeError when the calling task does not hold the condition's lock. The docstring in-source notes awakened tasks only resume after reacquiring the lock, so notifying without ownership would break the wakeup protocol.","triggerScenarios":"cond.notify() outside 'async with cond'; notify after the async-with body completed; producer task notifying a condition whose lock a consumer still holds.","commonSituations":"Porting threading code where notify is called loosely; splitting produce/consume across tasks without sharing the async with block; early returns that skip the context manager but still notify in cleanup.","solutions":["Structure producers as: async with cond: ...; cond.notify_all()","Ensure the notifying task is the one holding the lock (acquire it first)","Consider asyncio.Event or Queue when lock ownership is hard to guarantee"],"exampleFix":"# before\nasync def producer(item):\n    queue.append(item)\n    cond.notify_all()  # RuntimeError\n\n# after\nasync def producer(item):\n    async with cond:\n        queue.append(item)\n        cond.notify_all()","handlingStrategy":"validation","validationCode":"if not cond.locked():\n    # notify() would raise; wrap in 'async with cond' first","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Notify only inside the async-with block that holds the lock","Have the same task that holds the lock do the notify","Consider asyncio.Event/Queue when ownership is awkward"],"tags":["asyncio","condition-variable","synchronization"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}