From e6ae93f52a7e18e777bdc07337ceba1c268df24c Mon Sep 17 00:00:00 2001 From: Adityaj0 <93090622+Adityaj0@users.noreply.github.com> Date: Mon, 3 Aug 2026 12:54:41 -0700 Subject: [PATCH] fix: hybrid search minimum_nprobes(0) silently no-ops instead of raising (#3770) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary `LanceHybridQueryBuilder._create_query_builders()` checked `self._minimum_nprobes` for truthiness instead of `is not None` — the very next line correctly checks `is not None` for `self._maximum_nprobes`. Since `0` is falsy in Python, `.minimum_nprobes(0)` on a hybrid query silently dropped the value instead of forwarding it to the vector sub-query, where it would raise the same `ValueError` a plain vector query raises for the same input (`minimum_nprobes must be greater than 0`, validated in `rust/lancedb/src/query.rs` and covered for the plain-query path by `test_invalid_nprobes_sync`). Fixes #3766 ## Change One-line fix: `if self._minimum_nprobes:` → `if self._minimum_nprobes is not None:`, matching the existing `maximum_nprobes` check right below it. ## Test plan - [x] New regression test `test_hybrid_query_minimum_nprobes_zero_raises` in `python/python/tests/test_hybrid_query.py` - [x] `uv run --extra tests pytest python/tests/test_hybrid_query.py -vv` — 13 passed - [x] `uv run --extra dev ruff format` / `ruff check` — clean Co-authored-by: Claude Sonnet 5 --- python/python/lancedb/query.py | 2 +- python/python/tests/test_hybrid_query.py | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/python/python/lancedb/query.py b/python/python/lancedb/query.py index 60c5d0c68..095a7b5ff 100644 --- a/python/python/lancedb/query.py +++ b/python/python/lancedb/query.py @@ -2697,7 +2697,7 @@ class LanceHybridQueryBuilder(LanceQueryBuilder): self._fts_query.phrase_query(True) if self._distance_type: self._vector_query.metric(self._distance_type) - if self._minimum_nprobes: + if self._minimum_nprobes is not None: self._vector_query.minimum_nprobes(self._minimum_nprobes) if self._maximum_nprobes is not None: self._vector_query.maximum_nprobes(self._maximum_nprobes) diff --git a/python/python/tests/test_hybrid_query.py b/python/python/tests/test_hybrid_query.py index aa3e3f5fd..65a7890bf 100644 --- a/python/python/tests/test_hybrid_query.py +++ b/python/python/tests/test_hybrid_query.py @@ -123,6 +123,19 @@ async def test_async_hybrid_query_default_limit(table: AsyncTable): assert texts.count("a") == 1 +def test_hybrid_query_minimum_nprobes_zero_raises(sync_table: Table): + # minimum_nprobes(0) must raise the same validation error a plain vector + # query raises, not silently no-op because 0 is falsy. + with pytest.raises(ValueError, match="minimum_nprobes must be greater than 0"): + ( + sync_table.search(query_type="hybrid") + .vector([0.0, 0.4]) + .text("dog") + .minimum_nprobes(0) + .to_arrow() + ) + + def test_hybrid_query_distance_range(sync_table: Table): reranker = RRFReranker(return_score="all") result = (