Structured error handling and typed REST response models#71
Merged
Conversation
Backend failures no longer surface as opaque 500s: a new app/errors.py
classifier sorts concrete SDK exceptions (qdrant-client/httpx -> 503
backend_unavailable, openai/anthropic -> 502 upstream_provider_error,
else 500 internal_error) and an app-level exception handler returns a
stable sanitized JSON body carrying the request_id for log correlation —
exception text never reaches the client.
PUT/DELETE on a missing memory ID now 404 like GET (mem0's update/delete
otherwise raises or silently no-ops depending on version), and the MCP
get/update/delete tools return a structured {"error": "not_found"}
instead of nulls or raw tool exceptions.
REST routes gain pass-through Pydantic response models (extra=allow +
response_model_exclude_unset) so /docs publishes real schemas while the
wire format stays byte-identical to what mem0 returns.
Also fixes a latent test-isolation bug: the shared mem fixture only did
reset_mock(), which keeps return_value/side_effect across tests.
Closes #68
https://claude.ai/code/session_01H2Dbh6kD8bseWZZEf7kGhx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #68
What
Stable, sanitized errors. Backend failures previously surfaced as opaque 500s that could leak internals. Now every failure returns:
{"detail": "...", "error": "machine_code", "request_id": "abc123def456"}errorbackend_unavailableopenai.OpenAIError/anthropic.AnthropicError)upstream_provider_errorinternal_errorException text never reaches the client; the full traceback is logged server-side with the same
request_idthat's in the body (settable viaX-Request-Id), so errors correlate with log lines.404 consistency.
PUT/DELETEon a missing memory ID now 404 likeGET(mem0 otherwise raises or silently no-ops depending on version). The MCPget/update/deletetools return a structured{"error": "not_found", "memory_id": ...}instead ofnullor a raw tool exception, giving the model a usable signal.Typed responses. Every REST route gains a Pydantic response model —
MemoryItem,MemoryResults,AddMemoryResponse(incl. the dedup-marker fields),UpdateResponse,DeleteResponse,HistoryResponse— so/docspublishes real schemas for client generators and n8n. Models useextra="allow"+response_model_exclude_unset=True, so the wire format stays byte-identical to what mem0 returns: unknown fields pass through, absent fields aren't fabricated as nulls (covered by an explicit pass-through test).Design note (deviation from the issue plan)
The issue proposed wrapping every mem0 call site in a translator. Implemented instead as a single app-level exception handler over a classifier of concrete SDK exception types (
app/errors.py) — same behavior, zero call-site discipline required, and new mem0 call sites are covered automatically. Provider errors are checked before network errors because the openai/anthropic SDKs wrap httpx exceptions.Also fixes a latent test-isolation bug found along the way: the shared
memfixture only calledreset_mock(), which preservesreturn_value/side_effectacross tests.Tests
15 new tests (
tests/test_errors.py+ REST/MCP additions): classifier units (incl. provider-wins-over-wrapped-network ordering), 503/502/500 through the real app, no-leak assertion on exception text,X-Request-Idround-trip, REST 404s for update/delete, MCPnot_foundreturns, unknown-field pass-through. Full suite: 181 passed,ruffclean.Review
Adversarial review surfaced 7 candidates; 2 were fixed (added
httpx.HTTPStatusErrorto the backend tuple; explicitis Nonecheck in the MCP get tool so a falsy-but-present result isn't misreported as missing). The rest were deliberate tradeoffs (e.g. response validation strictness is bounded to documented field types; history rows stay untyped because their shape is mem0-version-dependent).https://claude.ai/code/session_01H2Dbh6kD8bseWZZEf7kGhx
Generated by Claude Code