redis/redis-py · warning · LockError
Cannot reacquire a lock with no timeout
Error message
Cannot reacquire a lock with no timeout
What it means
Raised as LockError by Lock.reacquire() when self.timeout is None. reacquire() resets the TTL back to the lock's configured timeout; if the Lock has no timeout (manual-release-only), there is no TTL value to reset to, so the operation is undefined and rejected.
Source
Thrown at redis/asyncio/lock.py:334
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
- Construct the Lock with a finite timeout so reacquire() has a TTL to reset to.
- If you need to (re)set a TTL on a no-timeout lock, acquire it with a timeout instead.
- Avoid reacquire() on manual-release locks by design.
Example fix
// before lock = Lock(redis, 'k') # timeout=None default await lock.acquire() lock.reacquire() # LockError // after lock = Lock(redis, 'k', timeout=30) await lock.acquire() lock.reacquire()
Defensive patterns
Strategy: validation
Validate before calling
def can_reacquire(lock) -> bool:
return lock.timeout is not None
if not can_reacquire(lock):
lock = Lock(redis, name, timeout=30) # recreate with a TTL
await lock.acquire()
await lock.reacquire() Type guard
def lock_has_timeout(lock) -> bool:
return lock.timeout is not None Try / catch
from redis.exceptions import LockError
try:
lock.reacquire()
except LockError as e:
if 'no timeout' in str(e):
# recreate the lock with a timeout
... Prevention
- Construct Lock with a finite timeout if you intend to reacquire.
- Avoid reacquire() on manual-release (timeout=None) locks.
- Reacquire with a timeout if you need to add a TTL later.
When it happens
Trigger: Constructing Lock(redis, name) with the default timeout=None and calling lock.reacquire(). Fires at lock.py:333-334.
Common situations: Using a perpetual lock and later wanting to refresh its TTL via reacquire; copying a no-timeout lock into a keepalive flow.
Related errors
- Unable to acquire lock within the time specified
- Cannot extend a lock with no timeout
- Cannot reacquire an unlocked lock
- No connection available.
- Cannot release a lock that's not owned or is already unlocke
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/b0ade59d493f5a8e.json.
Report an issue: GitHub.