tqdm/tqdm · critical · RuntimeError
cannot release un-acquired lock
Error message
cannot release un-acquired lock
What it means
This is tqdm.contrib.concurrent's thread-ownership lock: release() checks the releasing thread owns the lock; releasing from a different thread (or without acquire) raises RuntimeError 'cannot release un-acquired lock'.
Source
Thrown at tqdm/contrib/concurrent.py:56
return True
try:
if not blocking:
self._queue.get_nowait()
elif timeout == -1:
self._queue.get()
else:
remaining = max(0, timeout - (self._time() - start))
self._queue.get(timeout=remaining)
except Empty:
self._lock.release()
return False
self._owner = self.get_ident()
self._depth = 1
return True
def release(self):
if self._owner != self.get_ident():
raise RuntimeError("cannot release un-acquired lock")
self._depth -= 1
if not self._depth:
self._owner = None
self._queue.put(None)
self._lock.release()
def __enter__(self):
self.acquire()
return self
def __exit__(self, *exc):
self.release()
@contextmanager
def ensure_lock(tqdm_class, lock_name="", lock=None):
"""get (create if necessary) and then restore `tqdm_class`'s lock"""
old_lock = getattr(tqdm_class, '_lock', None) # don't create a new lockView on GitHub (pinned to 96f2e60e45)
Solutions
- Only release from the thread that acquired; rely on the context manager (with lock:)
- Avoid calling internal _lock methods of tqdm.contrib.concurrent directly
- Report a bug if this occurs with plain thread_map usage
Example fix
# before
lock.acquire()
threading.Thread(target=lock.release).start()
# after
with lock:
do_work() Defensive patterns
Strategy: try-catch
Try / catch
try:
with ops_lock: # always use context manager
do_work()
except RuntimeError as e:
if 'un-acquired' in str(e):
log.exception('lock misuse in thread %s', threading.get_ident())
raise Prevention
- Never call acquire/release manually on tqdm.contrib.concurrent internals
- Keep acquire and release on the same thread
- Prefer thread_map's public API over lock juggling
When it happens
Trigger: Calling release() on the ThreadOps lock from a thread other than the acquirer, or double-release after the depth counter hit zero, when using tqdm.contrib.concurrent.thread_map.
Common situations: Mixing manual acquire/release around thread_map; callbacks that spawn work in other threads; misuse of the internal API.
Related errors
- Set changed size during iteration (see https://github.com/tq
- create_th_lock not needed anymore
- tqdm:disabling monitor support (monitor_interval = 0) due to
- AttributeError ignored
- {val} : {typ}
AI-assisted analysis of tqdm/tqdm@96f2e60e45 (2026-08-28).
Data as JSON: /api/errors/b939def748cc05a9.
Report an issue: GitHub.