diff --git a/mem0/client/utils.py b/mem0/client/utils.py index 06a1c0ef2e..bada6158b0 100644 --- a/mem0/client/utils.py +++ b/mem0/client/utils.py @@ -6,13 +6,14 @@ NetworkError, create_exception_from_response, ) +from functools import wraps logger = logging.getLogger(__name__) class APIError(Exception): """Exception raised for errors in the API. - + Deprecated: Use specific exception classes from mem0.exceptions instead. This class is maintained for backward compatibility. """ @@ -22,15 +23,14 @@ class APIError(Exception): def api_error_handler(func): """Decorator to handle API errors consistently. - + This decorator catches HTTP and request errors and converts them to appropriate structured exception classes with detailed error information. - + The decorator analyzes HTTP status codes and response content to create the most specific exception type with helpful error messages, suggestions, and debug information. """ - from functools import wraps @wraps(func) def wrapper(*args, **kwargs): @@ -38,7 +38,7 @@ def wrapper(*args, **kwargs): return func(*args, **kwargs) except httpx.HTTPStatusError as e: logger.error(f"HTTP error occurred: {e}") - + # Extract error details from response response_text = "" error_details = {} @@ -47,7 +47,7 @@ def wrapper(*args, **kwargs): "url": str(e.request.url), "method": e.request.method, } - + try: response_text = e.response.text # Try to parse JSON response for additional error details @@ -59,7 +59,7 @@ def wrapper(*args, **kwargs): except (json.JSONDecodeError, AttributeError): # Fallback to plain text response pass - + # Add rate limit information if available if e.response.status_code == 429: retry_after = e.response.headers.get("Retry-After") @@ -68,13 +68,13 @@ def wrapper(*args, **kwargs): debug_info["retry_after"] = int(retry_after) except ValueError: pass - + # Add rate limit headers if available for header in ["X-RateLimit-Limit", "X-RateLimit-Remaining", "X-RateLimit-Reset"]: value = e.response.headers.get(header) if value: debug_info[header.lower().replace("-", "_")] = value - + # Create specific exception based on status code exception = create_exception_from_response( status_code=e.response.status_code, @@ -82,34 +82,46 @@ def wrapper(*args, **kwargs): details=error_details, debug_info=debug_info, ) - + raise exception - + except httpx.RequestError as e: logger.error(f"Request error occurred: {e}") - + # Determine the appropriate exception type based on error type + + orig_err = str(e) + # Type checking for exception subtypes is unavoidable here but make as efficient as possible if isinstance(e, httpx.TimeoutException): raise NetworkError( - message=f"Request timed out: {str(e)}", + message=f"Request timed out: {orig_err}", error_code="NET_TIMEOUT", suggestion="Please check your internet connection and try again", - debug_info={"error_type": "timeout", "original_error": str(e)}, + debug_info={ + "error_type": "timeout", + "original_error": orig_err, + }, ) elif isinstance(e, httpx.ConnectError): raise NetworkError( - message=f"Connection failed: {str(e)}", + message=f"Connection failed: {orig_err}", error_code="NET_CONNECT", suggestion="Please check your internet connection and try again", - debug_info={"error_type": "connection", "original_error": str(e)}, + debug_info={ + "error_type": "connection", + "original_error": orig_err, + }, ) else: # Generic network error for other request errors raise NetworkError( - message=f"Network request failed: {str(e)}", + message=f"Network request failed: {orig_err}", error_code="NET_GENERIC", suggestion="Please check your internet connection and try again", - debug_info={"error_type": "request", "original_error": str(e)}, + debug_info={ + "error_type": "request", + "original_error": orig_err, + }, ) return wrapper