Files
mem0ai--mem0/server/errors.py
T
wehub-resource-sync 555e282cc4
ci / changelog_check (push) Waiting to run
ci / check_changes (push) Waiting to run
ci / build_mem0 (3.10) (push) Blocked by required conditions
ci / build_mem0 (3.11) (push) Blocked by required conditions
ci / build_mem0 (3.12) (push) Blocked by required conditions
CLI Node CI / lint (push) Waiting to run
CLI Node CI / test (20) (push) Waiting to run
CLI Node CI / test (22) (push) Waiting to run
CLI Node CI / build (push) Waiting to run
CLI Python CI / lint (push) Waiting to run
CLI Python CI / test (3.10) (push) Waiting to run
CLI Python CI / test (3.11) (push) Waiting to run
CLI Python CI / test (3.12) (push) Waiting to run
CLI Python CI / build (push) Waiting to run
openclaw checks / lint (push) Waiting to run
openclaw checks / test (20) (push) Waiting to run
openclaw checks / test (22) (push) Waiting to run
openclaw checks / build (push) Waiting to run
opencode-plugin checks / build (push) Waiting to run
pi-agent-plugin checks / lint (push) Waiting to run
pi-agent-plugin checks / test (20) (push) Waiting to run
pi-agent-plugin checks / test (22) (push) Waiting to run
pi-agent-plugin checks / build (push) Waiting to run
TypeScript SDK CI / check_changes (push) Waiting to run
TypeScript SDK CI / changelog_check (push) Blocked by required conditions
TypeScript SDK CI / build_ts_sdk (20) (push) Blocked by required conditions
TypeScript SDK CI / build_ts_sdk (22) (push) Blocked by required conditions
TypeScript SDK CI / integration_ts_sdk (20) (push) Blocked by required conditions
TypeScript SDK CI / integration_ts_sdk (22) (push) Blocked by required conditions
chore: import upstream snapshot with attribution
2026-07-13 13:03:45 +08:00

96 lines
3.5 KiB
Python

import logging
import sys
import uuid
from contextvars import ContextVar
from fastapi import HTTPException, Request
from fastapi.responses import JSONResponse
request_id_var: ContextVar[str] = ContextVar("request_id", default="-")
class UpstreamError(HTTPException):
def __init__(self, code: str, detail: str, request_id: str) -> None:
super().__init__(status_code=502, detail=detail)
self.code = code
self.request_id = request_id
_AUTH_NAMES = {"AuthenticationError", "PermissionDeniedError"}
_RATE_NAMES = {"RateLimitError"}
_TIMEOUT_NAMES = {"APITimeoutError"}
_CONN_NAMES = {"APIConnectionError", "ConnectionError"}
_BAD_REQUEST_NAMES = {"BadRequestError", "UnprocessableEntityError"}
_DB_NAMES = {"OperationalError", "DBAPIError", "DisconnectionError"}
_VECTOR_NAMES = {"UnexpectedResponse", "ResponseHandlingException"}
def _classify_one(exc: BaseException) -> tuple[str, str]:
name = type(exc).__name__
module = getattr(type(exc), "__module__", "") or ""
status = getattr(exc, "status_code", None)
if name in _AUTH_NAMES or status in (401, 403):
return (
"provider_auth_failed",
"Provider rejected the request (authentication). "
"Check your LLM provider API key on the Configuration page.",
)
if name in _RATE_NAMES or status == 429:
return ("provider_rate_limited", "Provider rate limit hit. Retry shortly.")
if name in _TIMEOUT_NAMES or isinstance(exc, TimeoutError):
return ("provider_timeout", "Provider timed out. Retry shortly.")
if name in _CONN_NAMES or (isinstance(status, int) and status >= 500):
return ("provider_unavailable", "Provider is unreachable or returned a server error.")
if name in _BAD_REQUEST_NAMES or status in (400, 422):
return ("provider_bad_request", "Provider rejected the request as malformed.")
if name in _DB_NAMES:
return ("datastore_unavailable", "The memory database is unreachable.")
if name in _VECTOR_NAMES or module.startswith("qdrant_client"):
return ("vector_store_unavailable", "The vector store is unreachable or returned an error.")
return ("unknown", "Upstream provider error.")
def _classify(exc: BaseException | None) -> tuple[str, str]:
# Walk the cause/context chain so wrapped provider errors still classify correctly.
seen: set[int] = set()
current = exc
while current is not None and id(current) not in seen:
seen.add(id(current))
result = _classify_one(current)
if result[0] != "unknown":
return result
current = current.__cause__ or current.__context__
return ("unknown", "Upstream provider error.")
def new_request_id() -> str:
return uuid.uuid4().hex[:8]
def upstream_error() -> UpstreamError:
exc = sys.exc_info()[1]
code, message = _classify(exc)
rid = request_id_var.get()
logging.exception("Upstream provider error (code=%s)", code)
return UpstreamError(code=code, detail=message, request_id=rid)
async def upstream_error_handler(_: Request, exc: UpstreamError) -> JSONResponse:
return JSONResponse(
status_code=exc.status_code,
content={"detail": exc.detail, "code": exc.code, "request_id": exc.request_id},
headers={"X-Request-ID": exc.request_id},
)
def install_request_id_logging() -> None:
old_factory = logging.getLogRecordFactory()
def factory(*args, **kwargs):
record = old_factory(*args, **kwargs)
record.request_id = request_id_var.get()
return record
logging.setLogRecordFactory(factory)