redis/redis-py · error · LockError

Unable to acquire lock within the time specified

Error message

Unable to acquire lock within the time specified

What it means

When a Lock is used as an async context manager, __aenter__ calls acquire(); if it returns False (the blocking_timeout elapsed without winning the lock), LockError is raised so the 'async with' body never executes. A direct (non-context-manager) acquire() instead returns False, so this error specifically signals context-manager acquisition timeout.

Solutions

  1. Increase blocking_timeout when constructing the Lock.
  2. Ensure locks are released promptly and the token/thread_local setup is correct.
  3. Catch LockError and degrade gracefully instead of failing the request.
  4. Shorten the critical section protected by the lock.

Example fix

# before
lock = Lock(r, 'resource', blocking_timeout=1)
async with lock:  # raises LockError under contention
    ...
# after
from redis.exceptions import LockError
lock = Lock(r, 'resource', blocking_timeout=30)
try:
    async with lock:
        ...
except LockError:
    ...  # handle contention gracefully
Defensive patterns

Strategy: try-catch

Try / catch

from redis.exceptions import LockError
try:
    async with lock:
        ...
except LockError:
    # contention timeout: degrade gracefully
    ...

Prevention

When it happens

Trigger: 'async with lock:' where contention lasts longer than the lock's blocking_timeout (or default), so acquire() exhausts its retries and returns False inside __aenter__.

Common situations: High lock contention; forgotten or delayed release; critical sections longer than expected; blocking_timeout set too low; deadlock.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/79b00ee8bf2de581. Report an issue: GitHub.

Appendix: source

Thrown at redis/asyncio/lock.py:173

        self.local = threading.local() if self.thread_local else SimpleNamespace()
        self.raise_on_release_error = raise_on_release_error
        self.local.token = None
        self.register_scripts()

    def register_scripts(self):
        cls = self.__class__
        client = self.redis
        if cls.lua_release is None:
            cls.lua_release = client.register_script(cls.LUA_RELEASE_SCRIPT)
        if cls.lua_extend is None:
            cls.lua_extend = client.register_script(cls.LUA_EXTEND_SCRIPT)
        if cls.lua_reacquire is None:
            cls.lua_reacquire = client.register_script(cls.LUA_REACQUIRE_SCRIPT)

    async def __aenter__(self):
        if await self.acquire():
            return self
        raise LockError("Unable to acquire lock within the time specified")

    async def __aexit__(self, exc_type, exc_value, traceback):
        try:
            await self.release()
        except LockError:
            if self.raise_on_release_error:
                raise
            logger.warning(
                "Lock was unlocked or no longer owned when exiting context manager."
            )

    async def acquire(
        self,
        blocking: Optional[bool] = None,
        blocking_timeout: Optional[Number] = None,
        token: Optional[Union[str, bytes]] = None,
    ):
        """

View on GitHub (pinned to 6a6b581b48)