{"record":{"id":"382d89a380ef7d0c","repo":"python/cpython","slug":"cannot-wait-on-un-acquired-lock","errorCode":null,"errorMessage":"cannot wait on un-acquired lock","messagePattern":"cannot wait on un-acquired lock","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/locks.py","lineNumber":261,"sourceCode":"        return f'<{res[1:-1]} [{extra}]>'\n\n    async def wait(self):\n        \"\"\"Wait until notified.\n\n        If the calling task has not acquired the lock when this\n        method is called, a RuntimeError is raised.\n\n        This method releases the underlying lock, and then blocks\n        until it is awakened by a notify() or notify_all() call for\n        the same condition variable in another task.  Once\n        awakened, it re-acquires the lock and returns True.\n\n        This method may return spuriously,\n        which is why the caller should always\n        re-check the state and be prepared to wait() again.\n        \"\"\"\n        if not self.locked():\n            raise RuntimeError('cannot wait on un-acquired lock')\n\n        fut = self._get_loop().create_future()\n        self.release()\n        try:\n            try:\n                self._waiters.append(fut)\n                try:\n                    await fut\n                    return True\n                finally:\n                    self._waiters.remove(fut)\n\n            finally:\n                # Must re-acquire lock even if wait is cancelled.\n                # We only catch CancelledError here, since we don't want any\n                # other (fatal) errors with the future to cause us to spin.\n                err = None\n                while True:","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/python/cpython/blob/bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6/Lib/asyncio/locks.py#L243-L279","documentation":"asyncio.Condition.wait() requires the calling task to hold the condition's lock: it releases it before sleeping and re-acquires on wake. Calling wait() without a prior acquire() would release a lock you do not own, corrupting synchronization, so it raises RuntimeError immediately.","triggerScenarios":"await cond.wait() without 'async with cond'; calling wait() after the with-block exited; a helper task waiting on a condition whose lock is held by a different task.","commonSituations":"Translating threading.Condition patterns where the lock is implicit; forgetting that asyncio.Condition bundles its own lock; calling wait() in except/finally after the context exited early.","solutions":["Always wrap: async with cond: await cond.wait()","Re-acquire before notify paths too: async with cond: cond.notify_all()","If using a separate lock, construct Condition(lock=that_lock) and hold it before waiting"],"exampleFix":"# before\nawait cond.wait()  # RuntimeError: un-acquired lock\n\n# after\nasync with cond:\n    await cond.wait()","handlingStrategy":"validation","validationCode":"if not cond.locked():\n    # wait() would raise; acquire the lock first (async with cond)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always 'async with cond:' around wait()","The same task must hold the lock while waiting","Give a shared lock to Condition(lock=...) if ownership spans helpers"],"tags":["asyncio","condition-variable","synchronization"],"backgroundTag":null,"analyzedSha":"bc6749cc3b5ae4a5e88a6cc2d5b3bebbe354eae6","analyzedAt":"2026-08-14T22:01:13.976Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}