Error Handling

Learn how to handle errors and edge cases gracefully in your application. Proper error handling ensures a better user experience and helps with debugging.

Error Types

The Memory Scope API uses standard HTTP status codes and returns structured error responses. SDKs provide typed exceptions for different error types.

400/422
Invalid Request

Malformed request or invalid data

Action: Fix the request and retry

401
Authentication Error

Invalid or missing API key

Action: Check API key configuration

403
Policy Denied

Purpose not allowed for scope

Action: Use allowed purpose or different scope

404
Not Found

Resource not found (e.g., invalid token)

Action: Check resource exists or create new

429
Rate Limit

Too many requests

Action: Implement exponential backoff and retry

500
Server Error

Unexpected server error

Action: Retry with exponential backoff

Comprehensive Error Handling

Comprehensive Error Handling
from memory_scope import MemoryScopeClient from memory_scope.exceptions import ( PolicyDeniedError, InvalidRequestError, AuthenticationError, NotFoundError, RateLimitError ) import time import logging logger = logging.getLogger(__name__) def read_memory_with_error_handling(user_id, scope, domain, purpose, max_retries=3): """Read memory with comprehensive error handling""" client = MemoryScopeClient(api_key=os.getenv("MEMORY_SCOPE_API_KEY")) for attempt in range(max_retries): try: result = client.read_memory( user_id=user_id, scope=scope, domain=domain, purpose=purpose ) return {"success": True, "data": result} except PolicyDeniedError as e: # Policy denial - expected in some cases logger.info(f"Policy denied: {e.message}") return { "success": False, "error": "policy_denied", "message": "Access denied for this purpose", "fallback": get_default_preferences() } except InvalidRequestError as e: # Invalid request - fix and don't retry logger.error(f"Invalid request: {e.message}") return { "success": False, "error": "invalid_request", "message": str(e), "retry": False } except AuthenticationError as e: # Auth error - don't retry, check configuration logger.error(f"Authentication failed: {e.message}") return { "success": False, "error": "authentication", "message": "API key is invalid", "retry": False } except NotFoundError as e: # Not found - may be expected logger.warning(f"Resource not found: {e.message}") return { "success": False, "error": "not_found", "message": str(e), "retry": False } except RateLimitError as e: # Rate limit - retry with backoff if attempt < max_retries - 1: wait_time = 2 ** attempt logger.warning(f"Rate limit exceeded. Waiting {wait_time}s...") time.sleep(wait_time) continue else: logger.error("Rate limit exceeded. Max retries reached.") return { "success": False, "error": "rate_limit", "message": "Rate limit exceeded. Please try again later." } except Exception as e: # Unexpected error - log and retry if appropriate logger.error(f"Unexpected error: {e}", exc_info=True) if attempt < max_retries - 1: wait_time = 2 ** attempt time.sleep(wait_time) continue else: return { "success": False, "error": "unexpected", "message": "An unexpected error occurred" } return {"success": False, "error": "max_retries", "message": "Max retries exceeded"}
Policy Denial Handling

Policy denials are expected in some cases. They're not errors - they're a security feature. Handle them gracefully with fallback behavior.

Policy Denial Handling
try: result = client.read_memory( user_id="user123", scope="preferences", domain="food", purpose="execute task to auto-order lunch" ) except PolicyDeniedError: # This is expected - preferences can't be used for task execution # Use default behavior or ask user for permission result = { "summary_struct": {}, "confidence": 0.0 } # Show message to user or use defaults
Rate Limit Handling

Implement exponential backoff when you receive a 429 response. Don't retry immediately.

Rate Limit Retry
import time def read_with_retry(max_retries=3): for attempt in range(max_retries): try: return client.read_memory(...) except RateLimitError: if attempt < max_retries - 1: # Exponential backoff: 1s, 2s, 4s wait_time = 2 ** attempt time.sleep(wait_time) else: raise Exception("Rate limit exceeded")
Empty Results Handling

When no memories exist or access has been revoked, the API returns empty structures. Handle this gracefully.

Empty Results Handling
result = client.read_memory(...) # Check if data is available if not result.summary_struct or len(result.summary_struct) == 0: # No data available - handle gracefully if result.confidence == 0.0: # No memories exist or access revoked show_message("No preferences available. Please provide your preferences.") # Or use defaults preferences = get_default_preferences() else: # Low confidence - incomplete data preferences = result.summary_struct show_message("Limited preferences available.") else: # Data available preferences = result.summary_struct