redis/redis-py · warning · LockError
Cannot reacquire an unlocked lock
Error message
Cannot reacquire an unlocked lock
What it means
Raised as LockError by Lock.reacquire() when self.local.token is None: reacquire() (reset TTL to the original timeout) requires the lock to currently be owned by this instance. Without a token there is nothing to reacquire.
Source
Thrown at redis/asyncio/lock.py:332
async def do_extend(self, additional_time, replace_ttl) -> Literal[True]:
additional_time = int(additional_time * 1000)
if not bool(
await self.lua_extend(
keys=[self.name],
args=[self.local.token, additional_time, replace_ttl and "1" or "0"],
client=self.redis,
)
):
raise LockNotOwnedError("Cannot extend a lock that's no longer owned")
return True
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 da03cdc7e8)
Solutions
- Call reacquire() only after a successful acquire() and before release(), in the same task (or use thread_local=False).
- Stop keepalive loops when the token is cleared.
- Guard with owned() if the ownership state is uncertain.
Example fix
// before lock = Lock(redis, 'k', timeout=30) lock.reacquire() # before acquire -> LockError // after await lock.acquire() lock.reacquire()
Defensive patterns
Strategy: validation
Validate before calling
async def reacquire_if_owned(lock):
if lock.local.token is None:
return False
if lock.timeout is None:
return False
return await lock.reacquire() Type guard
def lock_holds_token(lock) -> bool:
return getattr(getattr(lock, 'local', None), 'token', None) is not None Try / catch
from redis.exceptions import LockError
try:
lock.reacquire()
except LockError as e:
if 'unlocked' in str(e):
# reacquire after a fresh acquire() instead
... Prevention
- Call reacquire() only after acquire() and before release().
- Stop keepalive loops once the token is cleared.
- Use owned() before reacquire if state is uncertain.
When it happens
Trigger: Calling lock.reacquire() before acquire() or after release(). Fires at lock.py:331-332.
Common situations: A keepalive loop calling reacquire() after the lock was released; reacquiring from a different task under thread_local=True; logic error calling reacquire without a prior successful acquire.
Related errors
- Cannot release a lock that's not owned or is already unlocke
- Cannot release a lock that's no longer owned
- Cannot extend an unlocked lock
- Cannot extend a lock that's no longer owned
- Cannot reacquire a lock with no timeout
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/4be726eefc62b94b.json.
Report an issue: GitHub.