Skip to main content

Overview

The Mention API SDK provides a comprehensive exception hierarchy and built-in retry mechanisms to help you build resilient applications. This guide covers all exception types, retry strategies, and best practices for error handling.

Exception Hierarchy

All exceptions inherit from MentionError, making it easy to catch all SDK-related errors:
MentionError (Base exception)
├── MentionAPIError (API returned an error)
│   ├── MentionAuthError (401, 403)
│   ├── MentionNotFoundError (404)
│   └── MentionRateLimitError (429)
├── MentionValidationError (Request validation failed)
└── MentionConnectionError (Network/connection issues)

Exception Types

MentionError

Base exception for all Mention SDK errors. Attributes:
  • message (str): Human-readable error message
  • details (dict): Additional error details
from mention.exceptions import MentionError

try:
    client.get_alerts("account-id")
except MentionError as e:
    print(f"Error: {e.message}")
    print(f"Details: {e.details}")

MentionAPIError

Raised when the API returns an error response. Attributes:
  • status_code (int): HTTP status code
  • response_body (dict | str | None): Raw response body from API
  • message (str): Error message
  • details (dict): Additional details
from mention.exceptions import MentionAPIError

try:
    client.get_alert("account-id", "invalid-alert-id")
except MentionAPIError as e:
    print(f"API Error: {e.message}")
    print(f"Status Code: {e.status_code}")
    print(f"Response: {e.response_body}")

MentionAuthError

Raised for authentication and authorization failures (401, 403). Attributes:
  • status_code (int): 401 or 403
  • response_body (dict | str | None): Error response
  • message (str): Authentication error message
from mention.exceptions import MentionAuthError

try:
    client = MentionClient(access_token="invalid-token")
    client.get_account_me()
except MentionAuthError as e:
    print(f"Authentication failed: {e.message}")
    print(f"Status: {e.status_code}")
    # Handle token refresh or re-authentication

MentionNotFoundError

Raised when a requested resource doesn’t exist (404). Attributes:
  • status_code (int): 404
  • response_body (dict | str | None): Error response
  • message (str): Not found error message
from mention.exceptions import MentionNotFoundError

try:
    mention = client.get_mention("account-id", "alert-id", "nonexistent-id")
except MentionNotFoundError as e:
    print(f"Resource not found: {e.message}")
    # Handle missing resource gracefully

MentionRateLimitError

Raised when API rate limits are exceeded (429). Attributes:
  • status_code (int): 429
  • response_body (dict | str | None): Error response
  • retry_after (int | None): Seconds to wait before retrying (from Retry-After header)
  • message (str): Rate limit error message
from mention.exceptions import MentionRateLimitError
import time

try:
    mentions = client.get_mentions("account-id", "alert-id")
except MentionRateLimitError as e:
    print(f"Rate limit exceeded: {e.message}")
    if e.retry_after:
        print(f"Retry after {e.retry_after} seconds")
        time.sleep(e.retry_after)
        # Retry the request

MentionValidationError

Raised for request validation errors (invalid parameters, missing required fields).
from mention.exceptions import MentionValidationError

try:
    # This will raise validation error
    client = MentionClient(access_token="")  # Empty token
except MentionValidationError as e:
    print(f"Validation error: {e.message}")

MentionConnectionError

Raised for network and connection issues (timeouts, connection failures).
from mention.exceptions import MentionConnectionError

try:
    client = MentionClient(
        access_token="token",
        timeout=0.001  # Very short timeout
    )
    client.get_account_me()
except MentionConnectionError as e:
    print(f"Connection error: {e.message}")
    # Handle network issues, retry with backoff

Built-in Retry Logic

Both MentionClient and AsyncMentionClient include automatic retry logic with exponential backoff for transient failures.

Default Retry Behavior

  • Max retries: 3 attempts (configurable)
  • Retry delay: 1 second base delay (configurable)
  • Backoff: Exponential (delay doubles each retry)
  • Retried errors: Connection errors, timeouts, rate limits

Configuring Retries

from mention import MentionClient

client = MentionClient(
    access_token="token",
    max_retries=5,      # Retry up to 5 times
    retry_delay=2.0     # Start with 2 second delay
)

# Retry delays: 2s, 4s, 8s, 16s, 32s

Rate Limit Handling

The client automatically respects Retry-After headers for rate limit errors:
from mention import MentionClient
from mention.exceptions import MentionRateLimitError

client = MentionClient(
    access_token="token",
    max_retries=3
)

try:
    # If rate limited, client will automatically:
    # 1. Read Retry-After header
    # 2. Wait the specified duration
    # 3. Retry the request (up to max_retries)
    mentions = client.get_mentions("account-id", "alert-id")
except MentionRateLimitError as e:
    # Only raised if retries are exhausted
    print(f"Rate limit still exceeded after retries")
    print(f"Wait {e.retry_after} seconds before trying again")

Error Handling Patterns

Basic Error Handling

Catch specific exceptions for targeted handling:
from mention import MentionClient
from mention.exceptions import (
    MentionAuthError,
    MentionNotFoundError,
    MentionRateLimitError,
    MentionConnectionError,
    MentionError
)

def fetch_mentions_safely(client, account_id, alert_id):
    try:
        mentions = client.get_mentions(account_id, alert_id)
        return mentions.mentions
    
    except MentionAuthError as e:
        print(f"Authentication failed: {e.message}")
        # Refresh token or re-authenticate
        return None
    
    except MentionNotFoundError as e:
        print(f"Alert not found: {e.message}")
        return []
    
    except MentionRateLimitError as e:
        print(f"Rate limited. Retry after {e.retry_after}s")
        return None
    
    except MentionConnectionError as e:
        print(f"Connection error: {e.message}")
        # Retry later
        return None
    
    except MentionError as e:
        print(f"Unexpected error: {e.message}")
        return None

Custom Retry Logic

Implement your own retry logic with custom backoff strategies:
import time
from mention import MentionClient
from mention.exceptions import MentionConnectionError, MentionRateLimitError

def retry_with_backoff(func, max_attempts=5, base_delay=1.0):
    """Decorator for custom retry logic"""
    def wrapper(*args, **kwargs):
        for attempt in range(max_attempts):
            try:
                return func(*args, **kwargs)
            
            except MentionRateLimitError as e:
                if attempt == max_attempts - 1:
                    raise
                
                delay = e.retry_after or (base_delay * (2 ** attempt))
                print(f"Rate limited. Waiting {delay}s...")
                time.sleep(delay)
            
            except MentionConnectionError as e:
                if attempt == max_attempts - 1:
                    raise
                
                delay = base_delay * (2 ** attempt)
                print(f"Connection failed. Retrying in {delay}s...")
                time.sleep(delay)
        
        raise Exception(f"Failed after {max_attempts} attempts")
    
    return wrapper

@retry_with_backoff(max_attempts=5, base_delay=2.0)
def get_mentions_with_retry(client, account_id, alert_id):
    return client.get_mentions(account_id, alert_id)

# Usage
client = MentionClient(access_token="token")
mentions = get_mentions_with_retry(client, "account-id", "alert-id")

Graceful Degradation

Handle errors gracefully without crashing:
from mention import MentionClient
from mention.exceptions import MentionError

def process_alerts_with_fallback(client, account_id):
    """Process alerts with graceful error handling"""
    try:
        alerts_response = client.get_alerts(account_id)
    except MentionError as e:
        print(f"Failed to fetch alerts: {e.message}")
        return []  # Return empty list as fallback
    
    results = []
    for alert in alerts_response.alerts:
        try:
            mentions = client.get_mentions(account_id, alert.id, limit=10)
            results.append({
                "alert": alert.name,
                "mention_count": len(mentions.mentions)
            })
        except MentionError as e:
            # Log error but continue processing other alerts
            print(f"Failed to fetch mentions for {alert.name}: {e.message}")
            results.append({
                "alert": alert.name,
                "mention_count": 0,
                "error": str(e)
            })
    
    return results

Logging Errors

Integrate with Python’s logging system:
import logging
from mention import MentionClient
from mention.exceptions import MentionError, MentionAPIError

logger = logging.getLogger(__name__)

def fetch_with_logging(client, account_id, alert_id):
    try:
        mentions = client.get_mentions(account_id, alert_id)
        logger.info(f"Fetched {len(mentions.mentions)} mentions")
        return mentions
    
    except MentionAPIError as e:
        logger.error(
            f"API error: {e.message}",
            extra={
                "status_code": e.status_code,
                "response_body": e.response_body,
                "account_id": account_id,
                "alert_id": alert_id
            }
        )
        raise
    
    except MentionError as e:
        logger.error(f"Client error: {e.message}", exc_info=True)
        raise

Best Practices

Order exception handlers from most specific to most general:
# ✅ Good: Specific to general
try:
    client.get_alerts(account_id)
except MentionAuthError:
    # Handle auth errors
    pass
except MentionAPIError:
    # Handle other API errors
    pass
except MentionError:
    # Handle any Mention error
    pass

# ❌ Bad: General exception catches everything
try:
    client.get_alerts(account_id)
except MentionError:  # This catches all!
    pass
except MentionAuthError:  # Never reached
    pass
Always log or handle errors appropriately:
# ❌ Bad: Silent failure
try:
    mentions = client.get_mentions(account_id, alert_id)
except MentionError:
    pass  # Error is lost!

# ✅ Good: Log and handle
try:
    mentions = client.get_mentions(account_id, alert_id)
except MentionError as e:
    logger.error(f"Failed to fetch mentions: {e}")
    mentions = []  # Provide fallback
Prevent cascading failures with circuit breaker:
from datetime import datetime, timedelta
from mention.exceptions import MentionError

class CircuitBreaker:
    def __init__(self, failure_threshold=5, timeout=60):
        self.failure_count = 0
        self.failure_threshold = failure_threshold
        self.timeout = timeout
        self.last_failure_time = None
        self.state = "closed"  # closed, open, half-open
    
    def call(self, func, *args, **kwargs):
        if self.state == "open":
            if datetime.now() - self.last_failure_time > timedelta(seconds=self.timeout):
                self.state = "half-open"
            else:
                raise Exception("Circuit breaker is open")
        
        try:
            result = func(*args, **kwargs)
            self.on_success()
            return result
        except MentionError as e:
            self.on_failure()
            raise
    
    def on_success(self):
        self.failure_count = 0
        self.state = "closed"
    
    def on_failure(self):
        self.failure_count += 1
        self.last_failure_time = datetime.now()
        if self.failure_count >= self.failure_threshold:
            self.state = "open"
Configure timeouts based on your requirements:
# For interactive applications (fast failure)
client = MentionClient(
    access_token="token",
    timeout=10.0,
    max_retries=2
)

# For background jobs (more resilient)
client = MentionClient(
    access_token="token",
    timeout=60.0,
    max_retries=5
)

Testing Error Handling

Test your error handling with mock failures:
import pytest
from unittest.mock import Mock, patch
from mention import MentionClient
from mention.exceptions import MentionAuthError, MentionNotFoundError

def test_auth_error_handling():
    """Test handling of authentication errors"""
    client = MentionClient(access_token="token")
    
    with patch.object(client, '_get') as mock_get:
        mock_get.side_effect = MentionAuthError(
            "Invalid token",
            status_code=401,
            response_body={"error": "invalid_token"}
        )
        
        with pytest.raises(MentionAuthError) as exc_info:
            client.get_account_me()
        
        assert exc_info.value.status_code == 401
        assert "Invalid token" in str(exc_info.value)

def test_not_found_handling():
    """Test handling of 404 errors"""
    client = MentionClient(access_token="token")
    
    with patch.object(client, '_get') as mock_get:
        mock_get.side_effect = MentionNotFoundError(
            "Alert not found",
            status_code=404
        )
        
        result = fetch_mentions_safely(client, "account-id", "alert-id")
        assert result == []  # Should return empty list

Next Steps

Pagination

Learn efficient pagination techniques

API Reference

Explore complete API documentation

Build docs developers (and LLMs) love