fix(server): return 404/400 instead of 502 for not-found and invalid input (#5634)

This commit is contained in:
Yash Singh
2026-06-22 11:47:48 +05:30
committed by GitHub
parent 8a786bf72d
commit 1676b3168d
4 changed files with 115 additions and 2 deletions
+53
View File
@@ -536,6 +536,59 @@ async def test_async_update_nonexistent_memory_raises_error(mock_sqlite, mock_ll
mock_vector_store.update.assert_not_called()
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@patch('mem0.memory.storage.SQLiteManager')
def test_update_propagates_vector_store_failure(mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
"""A backing-store failure while fetching the memory during update must
surface as the original error, not be masked as a 'provide a valid
memory_id' ValueError. The REST layer relies on this so an outage maps to
5xx instead of a misleading 4xx."""
mock_embedder_factory.return_value = MagicMock()
mock_vector_store = MagicMock()
mock_vector_factory.return_value = mock_vector_store
mock_llm_factory.return_value = MagicMock()
mock_sqlite.return_value = MagicMock()
from mem0.memory.main import Memory as MemoryClass
config = MemoryConfig()
memory = MemoryClass(config)
mock_vector_store.get.side_effect = ConnectionError("vector store unreachable")
with pytest.raises(ConnectionError, match="vector store unreachable"):
memory._update_memory("mem-1", "new data", {"new data": [0.1, 0.2]})
mock_vector_store.update.assert_not_called()
@pytest.mark.asyncio
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@patch('mem0.memory.storage.SQLiteManager')
async def test_async_update_propagates_vector_store_failure(mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
"""Async twin: a backing-store failure during update re-raises the original
error instead of masking it as a ValueError."""
mock_embedder_factory.return_value = MagicMock()
mock_vector_store = MagicMock()
mock_vector_factory.return_value = mock_vector_store
mock_llm_factory.return_value = MagicMock()
mock_sqlite.return_value = MagicMock()
from mem0.memory.main import AsyncMemory
config = MemoryConfig()
memory = AsyncMemory(config)
mock_vector_store.get.side_effect = ConnectionError("vector store unreachable")
with pytest.raises(ConnectionError, match="vector store unreachable"):
await memory._update_memory("mem-1", "new data", {"new data": [0.1, 0.2]})
mock_vector_store.update.assert_not_called()
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
+43
View File
@@ -12,6 +12,8 @@ from unittest.mock import MagicMock, patch
import pytest
from mem0.exceptions import ValidationError as Mem0ValidationError
pytest.importorskip("fastapi", reason="fastapi not installed")
from fastapi.testclient import TestClient
@@ -701,3 +703,44 @@ class TestSearchValidationErrors:
)
resp = client.post("/search", json={"query": "food"})
assert resp.status_code == 400
# ===========================================================================
# add / update / delete: map core errors to 4xx instead of 502
# ===========================================================================
class TestWriteHandlerErrorMapping:
"""ValueError("... not found") -> 404, other ValueError / Mem0ValidationError
-> 400. A real outage still surfaces as 502 via upstream_error()."""
def test_update_not_found_returns_404(self, client, mock_memory):
mock_memory.update.side_effect = ValueError("Memory with id mem-1 not found")
resp = client.put("/memories/mem-1", json={"text": "new"})
assert resp.status_code == 404
assert "not found" in resp.json()["detail"]
def test_delete_not_found_returns_404(self, client, mock_memory):
mock_memory.delete.side_effect = ValueError("Memory with id mem-1 not found")
resp = client.delete("/memories/mem-1")
assert resp.status_code == 404
def test_update_other_value_error_returns_400(self, client, mock_memory):
mock_memory.update.side_effect = ValueError("data must be a non-empty string")
resp = client.put("/memories/mem-1", json={"text": "new"})
assert resp.status_code == 400
def test_add_validation_error_returns_400(self, client, mock_memory):
mock_memory.add.side_effect = Mem0ValidationError(
message="messages must be str, dict, or list[dict]", error_code="VALIDATION_003"
)
resp = client.post("/memories", json={
"messages": [{"role": "user", "content": "hi"}], "user_id": "u1",
})
assert resp.status_code == 400
def test_add_real_outage_still_returns_502(self, client, mock_memory):
mock_memory.add.side_effect = RuntimeError("vector store unreachable")
resp = client.post("/memories", json={
"messages": [{"role": "user", "content": "hi"}], "user_id": "u1",
})
assert resp.status_code == 502