You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Debugger hangs when inspecting LanceDBConnection object on Python 3.13 #2053
The VS Code Python debugger hangs when a breakpoint is hit after creating a LanceDB connection. The issue only occurs with Python 3.13.5. The same code works correctly with Python 3.11.
The program runs normally outside the debugger.
Environment
OS: Debian 13
Python: 3.13.5
VS Code Python Debugger extension: 2026.6.0
LanceDB: 0.26.1
Debugger: debugpy (latest version installed)
Steps to reproduce
Minimal example:
import lancedb
print("before connect")
db = lancedb.connect("testdb")
print("after connect")
Steps:
Open the file in VS Code.
Start debugging with F5.
Add a breakpoint on:
print("after connect")
Run the debugger.
Expected behavior
The debugger should stop at the breakpoint and allow normal debugging.
Actual behavior
The debugger stops at the breakpoint but hangs. VS Code becomes unresponsive while the breakpoint is active.
The program continues to run normally if no breakpoint is set.
Additional observations
The issue appears related to debugger inspection of the LanceDBConnection object.
Thanks for the great report — we reproduced this and tracked it down. The short version: it's not a slow/looping repr, it's a thread deadlock that happens while expanding the variable in the Variables view.
Why it happens specifically with LanceDBConnection
When you stop at a breakpoint, debugpy suspends all threads — including background daemon threads. LanceDBConnection runs its async work on a background asyncio event loop living in a daemon thread (LanceDBBackgroundEventLoop), and several of its attributes are @property getters (e.g. read_consistency_interval) that dispatch a coroutine to that loop and block on the result (asyncio.run_coroutine_threadsafe(coro, loop).result()).
When the debugger expands the object in the Variables view, it enumerates attributes with dir() + getattr(), which evaluates those property getters. The getter then blocks waiting on the background loop thread — but that thread is suspended by the debugger — so it waits forever. Deadlock.
That also explains the clues in the report:
repr(db) is instant → __repr__ never touches those properties.
vars(db) works → it only reads the instance __dict__ (storage_options, _conn), with no property evaluation.
Setting db = None before the breakpoint avoids the hang → nothing to expand.
The 3.13-vs-3.11 difference comes down to how the background loop thread is traced/suspended mid-coroutine (sys.monitoring on 3.12+ vs settrace on 3.11); the underlying deadlock is the same class of bug.
We also found a smaller contributing issue: the resolver evaluated each property twice (a hasattr() followed by getattr()), doubling the cost/side-effects of expansion.
If resolving a variable's children takes too long, debugpy now temporarily resumes the other threads until it finishes, then re-suspends them (new PYDEVD_UNBLOCK_THREADS_ON_VARIABLES_TIMEOUT, default 3s) — so the debugger recovers instead of hanging.
The resolver now calls getattr() a single time instead of hasattr() + getattr().
A regression test reproduces the exact scenario (a property that blocks on a suspended background thread).
Activity
rchiodo commentedon Aug 5, 2026
Thanks for the great report — we reproduced this and tracked it down. The short version: it's not a slow/looping
repr, it's a thread deadlock that happens while expanding the variable in the Variables view.Why it happens specifically with
LanceDBConnectionWhen you stop at a breakpoint, debugpy suspends all threads — including background daemon threads.
LanceDBConnectionruns its async work on a background asyncio event loop living in a daemon thread (LanceDBBackgroundEventLoop), and several of its attributes are@propertygetters (e.g.read_consistency_interval) that dispatch a coroutine to that loop and block on the result (asyncio.run_coroutine_threadsafe(coro, loop).result()).When the debugger expands the object in the Variables view, it enumerates attributes with
dir()+getattr(), which evaluates those property getters. The getter then blocks waiting on the background loop thread — but that thread is suspended by the debugger — so it waits forever. Deadlock.That also explains the clues in the report:
repr(db)is instant →__repr__never touches those properties.vars(db)works → it only reads the instance__dict__(storage_options,_conn), with no property evaluation.db = Nonebefore the breakpoint avoids the hang → nothing to expand.The 3.13-vs-3.11 difference comes down to how the background loop thread is traced/suspended mid-coroutine (
sys.monitoringon 3.12+ vssettraceon 3.11); the underlying deadlock is the same class of bug.We also found a smaller contributing issue: the resolver evaluated each property twice (a
hasattr()followed bygetattr()), doubling the cost/side-effects of expansion.Fix
PR #2055 addresses both:
PYDEVD_UNBLOCK_THREADS_ON_VARIABLES_TIMEOUT, default 3s) — so the debugger recovers instead of hanging.getattr()a single time instead ofhasattr()+getattr().A regression test reproduces the exact scenario (a property that blocks on a suspended background thread).
Fix debugger hang when expanding objects with blocking property gette…
chore(deps): update dependency debugpy to v1.8.22