Skip to content

[BUG] Memory scope validation mutates reusable configuration dictionaries #7204

Description

@tandede

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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions