Description
MemoryScope.model_validate() and MemorySlice.model_validate() mutate caller-owned configuration dictionaries during validation.
MemoryScope removes the memory dependency with pop(), MemorySlice removes the dependency and normalizes scopes in place, and legacy discriminator inference inserts memory_kind into the original mapping.
As a result, reusing the same configuration can produce a second memory view that is no longer connected to its original Memory instance.
Steps to Reproduce
from unittest.mock import MagicMock
from crewai.memory.memory_scope import MemoryScope, MemorySlice
memory = MagicMock()
scope_config = {
"memory": memory,
"root_path": "/agent/1",
}
first_scope = MemoryScope.model_validate(scope_config)
second_scope = MemoryScope.model_validate(scope_config)
print(scope_config)
print(first_scope._require_memory() is memory)
print(second_scope._require_memory() is memory)
slice_config = {
"memory": memory,
"scopes": ["/team/", "/"],
}
MemorySlice.model_validate(slice_config)
print(slice_config)
On the current main branch, the first validation removes memory from scope_config. Reusing it therefore constructs a second scope without its runtime memory dependency. MemorySlice similarly rewrites the caller's scopes list.
Expected behavior
Validation should not modify dictionaries owned by the caller. The same configuration should remain reusable, and every constructed memory view should retain the provided memory dependency.
Path normalization and legacy memory_kind inference should affect only the validated copy.
Screenshots/Code snippets
Observed state after validation:
scope_config == {"root_path": "/agent/1"}
slice_config == {"scopes": ["/team", "/"]}
Expected state:
scope_config == {
"memory": memory,
"root_path": "/agent/1",
}
slice_config == {
"memory": memory,
"scopes": ["/team/", "/"],
}
Operating System
Other: macOS 26.4.1
Python Version
3.12
crewAI Version
Current main (b608a3595c95085225e9dd47432d74989f1a1d78); local package metadata reports 1.15.17.
crewAI Tools Version
1.15.17
Virtual Environment
Venv managed by uv.
Evidence
Regression coverage demonstrates three mutation paths:
- repeated
MemoryScope validation loses the original memory dependency;
MemorySlice validation normalizes caller-owned paths in place;
- legacy discriminator inference inserts
memory_kind into the caller's mapping.
The focused fix makes a shallow copy immediately before each mutation. The memory test suite passes with the fix (150 passed), together with Ruff, formatting, and mypy checks.
Possible Solution
Create a shallow copy of dictionary inputs before:
- inserting the inferred
memory_kind;
- removing the runtime
memory dependency in MemoryScope;
- removing
memory and normalizing paths in MemorySlice.
This preserves caller-owned input while keeping validated values and public behavior unchanged.
Additional context
A focused implementation and regression tests are available in PR #7068.
AI assistance was used to inspect the validator mutation paths and draft regression coverage. I reproduced the behavior, reviewed the proposed changes, and ran the validation locally.
The required llm-generated label cannot be applied by an external contributor. Please add it during triage.
Description
MemoryScope.model_validate()andMemorySlice.model_validate()mutate caller-owned configuration dictionaries during validation.MemoryScoperemoves thememorydependency withpop(),MemorySliceremoves the dependency and normalizesscopesin place, and legacy discriminator inference insertsmemory_kindinto the original mapping.As a result, reusing the same configuration can produce a second memory view that is no longer connected to its original
Memoryinstance.Steps to Reproduce
On the current
mainbranch, the first validation removesmemoryfromscope_config. Reusing it therefore constructs a second scope without its runtime memory dependency.MemorySlicesimilarly rewrites the caller'sscopeslist.Expected behavior
Validation should not modify dictionaries owned by the caller. The same configuration should remain reusable, and every constructed memory view should retain the provided memory dependency.
Path normalization and legacy
memory_kindinference should affect only the validated copy.Screenshots/Code snippets
Observed state after validation:
Expected state:
Operating System
Other: macOS 26.4.1
Python Version
3.12
crewAI Version
Current
main(b608a3595c95085225e9dd47432d74989f1a1d78); local package metadata reports1.15.17.crewAI Tools Version
1.15.17
Virtual Environment
Venv managed by
uv.Evidence
Regression coverage demonstrates three mutation paths:
MemoryScopevalidation loses the original memory dependency;MemorySlicevalidation normalizes caller-owned paths in place;memory_kindinto the caller's mapping.The focused fix makes a shallow copy immediately before each mutation. The memory test suite passes with the fix (
150 passed), together with Ruff, formatting, and mypy checks.Possible Solution
Create a shallow copy of dictionary inputs before:
memory_kind;memorydependency inMemoryScope;memoryand normalizing paths inMemorySlice.This preserves caller-owned input while keeping validated values and public behavior unchanged.
Additional context
A focused implementation and regression tests are available in PR #7068.
AI assistance was used to inspect the validator mutation paths and draft regression coverage. I reproduced the behavior, reviewed the proposed changes, and ran the validation locally.
The required
llm-generatedlabel cannot be applied by an external contributor. Please add it during triage.