Skip to content

fix: prevent in-place mutation of metadata in _create_memory#4529

Merged
kartik-mem0 merged 1 commit intomainfrom
fix/metadata-mutation-in-create-memory
Mar 25, 2026
Merged

fix: prevent in-place mutation of metadata in _create_memory#4529
kartik-mem0 merged 1 commit intomainfrom
fix/metadata-mutation-in-create-memory

Conversation

@utkarsh240799
Copy link
Copy Markdown
Contributor

Problem

Closes #2648

The metadata dict passed by callers to _create_memory and _create_procedural_memory (both sync and async) was mutated in-place. These methods directly set keys like data, hash, created_at, and memory_type on the caller's original dict object.

This causes two problems:

  1. Concurrency/thread-safety: When the same metadata dict is shared across concurrent calls (common in async usage with asyncio.gather), one call's mutations bleed into another, producing corrupted payloads.
  2. Surprising side-effects: Even in sequential usage, the caller's dict is silently modified after the call returns, which violates the principle of least surprise.

Affected methods

Method Class Issue
_create_memory Memory metadata["data"] = ..., metadata["hash"] = ..., metadata["created_at"] = ...
_create_memory AsyncMemory Same as above
_create_procedural_memory Memory metadata["memory_type"] = ...
_create_procedural_memory AsyncMemory Same as above

Note: _update_memory and _add_to_vector_store already use deepcopy and are not affected.

Solution

Use deepcopy(metadata) at the entry point of each affected method before any mutation, storing the result in a new_metadata local variable. The caller's original dict is never touched.

# Before (mutates caller's dict)
metadata = metadata or {}
metadata["data"] = data

# After (safe copy)
new_metadata = deepcopy(metadata) if metadata is not None else {}
new_metadata["data"] = data

This is a minimal, backward-compatible change — no caller reads back from the metadata dict after passing it to these methods.

Testing

Added 12 new tests in TestMetadataNotMutated covering:

  • No-mutation guarantee: Verify the caller's metadata dict is identical before and after calling _create_memory, _update_memory, and _add_to_vector_store (sync + async)
  • Shared dict safety: Pass the same metadata dict to multiple sequential _create_memory calls and verify it remains unchanged, while each call stores the correct distinct payload
  • Nested structure protection: Verify deepcopy protects nested dicts and lists inside metadata
  • Payload correctness: Verify the stored payload still contains all expected fields (data, hash, created_at, role, actor_id, custom fields)
  • History correctness: Verify role and actor_id from metadata flow through correctly to add_history after deepcopy
  • None metadata: Verify metadata=None still works correctly
  • End-to-end no-infer path: Verify _add_to_vector_store(infer=False) with multiple messages doesn't leak metadata between iterations (sync + async)

All 109 existing + new tests pass.

🤖 Generated with Claude Code

…d _create_procedural_memory

Addresses #2648. The metadata dict passed by callers was mutated in-place
inside _create_memory and _create_procedural_memory (both sync and async),
causing unsafe behavior under concurrent/async usage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kartik-mem0 kartik-mem0 merged commit 2a36960 into main Mar 25, 2026
10 of 13 checks passed
farrrr added a commit to farrrr/mem0 that referenced this pull request Mar 25, 2026
* feat/falkordb-graph-store:
  feat(falkordb): per-user graph isolation using native multi-graph
  test+docs(falkordb): add unit tests, integration docs, and dependency
  fix(falkordb): handle dict and Pydantic embedder config in __init__
  style(falkordb): backtick-escape __Entity__ label in Cypher queries
  feat(falkordb): add fallback LLM for graph entity/relation extraction
  perf(falkordb): batch embedding for entity writes
  fix(falkordb): add defensive validation for incomplete LLM entity output
  feat(falkordb): add soft-delete relationships and timestamps
  refactor(falkordb): use list-based MERGE property assembly in _add_entities
  feat(graph): add FalkorDB as graph store provider
  fix: prevent in-place mutation of metadata in _create_memory (mem0ai#4529)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

handle metadata and new_message_embeddings in a thread/concurrent safe way

2 participants