Regression from #4805: _safe_deepcopy_config redacts the bool switch use_azure_credential, silently breaking config reconstruction
Component
sdk-python (mem0/memory/main.py — _safe_deepcopy_config / _is_sensitive_field)
Description
TL;DR: _safe_deepcopy_config() redacts the boolean switch use_azure_credential because of the _credential suffix, which makes the pydantic reconstruction throw and silently degrade the config to a plain object. This is a regression introduced by #4805 — the field was explicitly protected in _RUNTIME_FIELDS by #4418, and #4805 removed that line and flipped the official test expectation to match.
Trigger path
AzureMySQLConfigis constructed with a liveconnection_pool(a prebuilt pool is supported — the field isOptional[Any],mem0/configs/vector_stores/azure_mysql.py:29).Memory.__init__calls_safe_deepcopy_config()on the vector store config (e.g. via theentity_storeproperty,main.py:562/main.py:2248, or the async telemetry path atmain.py:2221).- The bare
deepcopy(config)fails on the live pool (TypeError: cannot pickle ...), so_safe_deepcopy_configfalls into the reconstruction path: it copies field-by-field, setting every "sensitive" field toNone. _is_sensitive_field("use_azure_credential")returnsTrue— it is not in the 18-entry_SENSITIVE_FIELDS_EXACTlist and not in_RUNTIME_FIELDS, so it matches purely on the_credentialsuffix. But the field is aboolswitch (use DefaultAzureCredential or not), not a credential.- Reconstructing
AzureMySQLConfig(**fields)now fails validation:
pydantic_core._pydantic_core.ValidationError: 1 validation error for AzureMySQLConfig
use_azure_credential
Input should be a valid boolean [type=bool_type, input_value=None, input_type=NoneType]The broad
except Exceptionatmain.py:294-298swallows this, and the function returnstype("Config", (), clone_dict)()— a plain object with no pydantic interface (model_dump, validation, typed fields all gone). (Side note: this fallback turns the dict into class attributes, so every such degraded object shares one dict — instances are not independent.)Downstream,
VectorStoreFactory.createcallsconfig.model_dump()(mem0/utils/factory.py:214) and raisesAttributeError: 'Config' object has no attribute 'model_dump'— beforeload_classand before any network call.
Affected call sites on current main (94c3fe9f)
| Location | Path | Effect |
|---|---|---|
main.py:562 |
sync entity_store property |
AttributeError on first access (11 self.entity_store references go through it) |
main.py:2248 |
async entity_store property |
same, async (13 references) |
main.py:2221 |
async telemetry | AttributeError at factory.py:214 |
main.py:532 |
sync telemetry | not affected — the return value is overwritten at main.py:538; that dead call is what #5871 tracks |
This is a regression from #4805, not a never-fixed gap
_RUNTIME_FIELDS was introduced by #4418 (merged 2026-03-19) and already contained "use_azure_credential", with the parameterized test expectation set to False ("not sensitive"). PR #4805 (merged 2026-04-14, "feat(oss): port v3 pipeline") removed exactly this one line and changed the test expectation at tests/memory/test_safe_deepcopy_config.py:169 to True, which fixed the wrong behavior into the test as the expected one:
commit 46b4b2e9 (2026-03-19, #4418): _RUNTIME_FIELDS contains "use_azure_credential"; test expects False
commit a488e190 (2026-04-14, #4805): line removed; test flipped to True
commit 94c3fe9f (current main): still absent; test still expects TrueSuggested fix (restores the pre-#4805 behavior)
Add "use_azure_credential" back to _RUNTIME_FIELDS and change the test expectation at tests/memory/test_safe_deepcopy_config.py:169 back to False. It is a bool switch and cannot carry a secret, so the allowlist is the semantically correct place — no heuristic (name-shape or type-based) is needed, and real credentials are unaffected (verified below). Not claiming this is the only possible fix, but it is the minimal one with git-history backing.
Boundary with existing reports: #3580 / #4418 are about http_auth on OpenSearch — same function, different field, not a duplicate. #5871 tracks the dead _safe_deepcopy_config call at main.py:532 — related location, unrelated defect. Nothing in the tracker covers use_azure_credential.
How You Verified This
On current origin/main (94c3fe9f238f3dbf29c9ce98643bd71eb13077cd), Python 3.12:
_is_sensitive_field('use_azure_credential')returnsTrue; the only matching rule isendswith('_credential')(not in_RUNTIME_FIELDS, not in_SENSITIVE_FIELDS_EXACT).- Built a real
AzureMySQLConfigwithconnection_pool=sqlite3.connect(":memory:")— a genuinely unpicklable live object, standing in for a prebuilt pool (no Azure service involved). The baredeepcopyfails withTypeError: cannot pickle 'sqlite3.Connection' object, which is the required precondition for the reconstruction path. - After
_safe_deepcopy_config, the clone is a plainConfigobject (type(clone).__mro__ == [Config, object]),use_azure_credential is None,password is None(expected — that one is a credential), andhasattr(clone, "model_dump") is False. - Reconstructing the config from the redacted fields reproduces the exact
ValidationErrorabove (reproduced the reconstruction loop frommain.py:279-295). - Feeding the degraded clone into
VectorStoreFactory.createraisesAttributeError: 'Config' object has no attribute 'model_dump'atmem0/utils/factory.py:214— beforeload_class, so no Azure/network access is involved in the failure. - Verified the suggested fix restores behavior: with
"use_azure_credential"back in_RUNTIME_FIELDSand the test expectation back toFalse, the fulltests/memory/test_safe_deepcopy_config.pysuite is green (99 items including 5 new regression tests) and a real credential field (password) is still redacted. - Checked the suffix blast radius with an AST scan over all of
mem0/configs/: of 25 fields that match either the exact list or the suffix list, only one is matched via a suffix, and it is thisboolfield. No other non-credential scalar is affected. - Collision check: searched open/closed issues and PRs (
use_azure_credential,safe_deepcopy_config,credential suffix,redact config,entity_store deepcopy, …, plus a full scan of the 8000-item issue/PR list) — no existing report covers this.
AI Assistance
AI-generated (an agent wrote most or all of this report): the reproduction, the AST blast-radius scan, the git regression history, and the fix verification above were produced by an AI agent working against a local clone at 94c3fe9f; every claim listed under "How You Verified This" comes from real runs logged locally, and the finding was reviewed and confirmed by the contributor. No cloud Azure service was used — the failure reproduces entirely offline.
Source: mem0ai/mem0