redis/redis-py · error · LockNotOwnedError

Cannot reacquire a lock that's no longer owned

Error message

Cannot reacquire a lock that's no longer owned

What it means

do_reacquire runs the Lua reacquire script; if the key is gone/TTL-expired or the token differs, it returns 0 and LockNotOwnedError is raised. The lock expired before the reacquire request reached the server, so the TTL could not be reset.

Solutions

  1. Reacquire before expiry with margin.
  2. Catch LockNotOwnedError and re-acquire the lock fresh.
  3. Use a longer base timeout to give renewal more room.
Defensive patterns

Strategy: try-catch

Try / catch

from redis.exceptions import LockNotOwnedError
try:
    await lock.reacquire()
except LockNotOwnedError:
    # lock expired before reacquire: re-acquire fresh
    await lock.acquire()

Prevention

When it happens

Trigger: Calling reacquire() after the lock TTL already expired; another holder took over the key between the last operation and reacquire.

Common situations: Renewal scheduled too late; long event-loop/GC pause pushing reacquire past expiry.

Related errors


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

Appendix: source

Thrown at redis/asyncio/lock.py:344

    def reacquire(self) -> Awaitable[Literal[True]]:
        """
        Resets a TTL of an already acquired lock back to a timeout value.
        """
        if self.local.token is None:
            raise LockError("Cannot reacquire an unlocked lock")
        if self.timeout is None:
            raise LockError("Cannot reacquire a lock with no timeout")
        return self.do_reacquire()

    async def do_reacquire(self) -> Literal[True]:
        timeout = int(self.timeout * 1000)
        if not bool(
            await self.lua_reacquire(
                keys=[self.name], args=[self.local.token, timeout], client=self.redis
            )
        ):
            raise LockNotOwnedError("Cannot reacquire a lock that's no longer owned")
        return True

View on GitHub (pinned to 6a6b581b48)