{"id":"e6f57114fc37494d","repo":"redis/redis-py","slug":"unable-to-acquire-lock-within-the-time-specified-e6f571","errorCode":null,"errorMessage":"Unable to acquire lock within the time specified","messagePattern":"Unable to acquire lock within the time specified","errorType":"exception","errorClass":"LockError","httpStatus":null,"severity":"warning","filePath":"redis/lock.py","lineNumber":170,"sourceCode":"        self.raise_on_release_error = raise_on_release_error\n        self.local = threading.local() if self.thread_local else SimpleNamespace()\n        self.local.token = None\n        self.register_scripts()\n\n    def register_scripts(self) -> None:\n        cls = self.__class__\n        client = self.redis\n        if cls.lua_release is None:\n            cls.lua_release = client.register_script(cls.LUA_RELEASE_SCRIPT)\n        if cls.lua_extend is None:\n            cls.lua_extend = client.register_script(cls.LUA_EXTEND_SCRIPT)\n        if cls.lua_reacquire is None:\n            cls.lua_reacquire = client.register_script(cls.LUA_REACQUIRE_SCRIPT)\n\n    def __enter__(self) -> \"Lock\":\n        if self.acquire():\n            return self\n        raise LockError(\n            \"Unable to acquire lock within the time specified\",\n            lock_name=self.name,\n        )\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_value: Optional[BaseException],\n        traceback: Optional[TracebackType],\n    ) -> None:\n        try:\n            self.release()\n        except LockError:\n            if self.raise_on_release_error:\n                raise\n            logger.warning(\n                \"Lock was unlocked or no longer owned when exiting context manager.\"\n            )","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/lock.py#L152-L188","documentation":"Raised as redis.exceptions.LockError by Lock.__enter__ (redis/lock.py:170). When using the lock as a context manager, __enter__ calls acquire(); if acquire() returns False (it could not obtain the lock within blocking_timeout, or blocking was False and the lock was held), __enter__ raises so the `with` block never executes. LockError subclasses ValueError.","triggerScenarios":"Using `with client.lock(name, blocking_timeout=N):` (or blocking=False) when the lock key is already held by another holder for longer than blocking_timeout, or when blocking=False and the lock is contended.","commonSituations":"A previous holder crashed without releasing (TTL still running); high contention on a hot lock name; blocking_timeout too short for the workload; deadlock between competing workers.","solutions":["Catch redis.exceptions.LockError around the `with` statement and degrade gracefully (skip, queue, or retry).","Increase blocking_timeout (or set a lock timeout so stale locks expire sooner).","Ensure lock holders always release in a finally block, and set a sensible lock timeout so crashed holders' locks expire.","Use a unique lock name per resource to avoid false contention.","If non-blocking semantics are wanted, handle the False return / LockError as 'busy, try later'."],"exampleFix":"// before\nwith client.lock('job-1', blocking_timeout=5):\n    do_work()  # LockError if not acquired in 5s\n\n// after\nfrom redis.exceptions import LockError\ntry:\n    with client.lock('job-1', blocking_timeout=30):\n        do_work()\nexcept LockError:\n    log.info('job-1 busy, skipping this run')","handlingStrategy":"try-catch","validationCode":"from redis.exceptions import LockError\n# probe non-blocking first to avoid the LockError from __enter__\nlock = client.lock('job-1')\nif not lock.acquire(blocking=False):\n    log.info('job-1 busy')\nelse:\n    try:\n        do_work()\n    finally:\n        lock.release()","typeGuard":null,"tryCatchPattern":"from redis.exceptions import LockError\ntry:\n    with client.lock('job-1', blocking_timeout=30):\n        do_work()\nexcept LockError:\n    log.info('job-1 busy, skipping')","preventionTips":["Catch LockError around the `with client.lock(...)` and degrade gracefully.","Set a lock timeout so crashed holders' locks expire; size blocking_timeout to the workload.","Use a unique lock name per resource to avoid false contention.","Always release in a finally block (or use the context manager) so locks don't leak.","For non-blocking semantics, acquire(blocking=False) and handle the False return explicitly."],"tags":["lock","distributed-lock","contention","lockerror","context-manager"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}