Repository navigation
Description
What happens
serve_catalog_ui sets server_started = True immediately after _start_duckdb_ui_server
returns (src/hflow/catalog_ui.py:132-133), and shutdown calls stop_ui_server() on that flag
alone (catalog_ui.py:90-95):
def _stop_duckdb_ui_server(catalog_connection, server_started) -> None:
try:
if server_started:
catalog_connection.execute("CALL stop_ui_server()")stop_ui_server() is a function the ui extension provides. Calling it on a connection that
never loaded the extension makes DuckDB go and install ui from the network, at shutdown,
to run a no-op: there is no server to stop if the extension was never loaded.
Why it is reachable, and where it bites
tests/test_catalog_ui.py:49 replaces the real starter:
monkeypatch.setattr(catalog_ui, "_start_duckdb_ui_server", record_server_start)The stub records the call and returns. It never runs INSTALL ui / LOAD ui, but
serve_catalog_ui still sets server_started = True, so every one of these tests reaches
CALL stop_ui_server() with no extension loaded and DuckDB fetches the extension over the
network while the test is tearing down.
That is almost certainly the test_catalog_ui timing flake three separate contributors reported
on 2026-08-31 (#326, #327, #328), all describing a thread-shutdown or timing failure that passed
on a rerun. A network fetch inside a teardown that a test is waiting on with a fixed timeout is
exactly that shape, and it makes the suite depend on network access it otherwise does not need.
What to build
Only call stop_ui_server() when the ui extension is actually loaded on that connection:
def _ui_extension_is_loaded(catalog_connection) -> bool:
row = catalog_connection.execute(
"SELECT count(*) FROM duckdb_extensions() WHERE extension_name = 'ui' AND loaded"
).fetchone()
return row is not None and int(row[0]) > 0and gate the call on server_started and _ui_extension_is_loaded(...).
Definition of done
- Shutdown does not call
stop_ui_server()when theuiextension is not loaded. - A real UI session still stops its server on shutdown.
- A test pins that the stubbed-starter path issues no
stop_ui_server()call. Recording the
statements executed on the connection is enough; asserting DuckDB's install behavior is not
the point. - Existing catalog UI startup, port-conflict, and refresh behavior is unchanged.
Validation
uv sync --locked --all-extras
uv run ruff check --fix
uv run ruff format
uv run ty check
uv run pytest -q
Run plain uv run pytest -q from the repo root.
Notes
Found by @rakesh0x while working on #291. The fix is written and validated already, it just
needs to travel separately from an examples change.
Activity
fix(catalog-ui): don't auto-install the ui extension at shutdown
fix(catalog-ui): don't auto-install the ui extension at shutdown (#359)