Exceptions
The Clio SDK defines a hierarchy of exception classes to help you handle different types of errors appropriately. All exceptions inherit from the base ClioError class.
Exception Hierarchy
Section titled “Exception Hierarchy”ClioError├── ClioAuthError├── ClioUploadError├── ClioRateLimitError└── ClioConfigErrorBase Exception
Section titled “Base Exception”ClioError
Section titled “ClioError”Base exception class for all Clio SDK errors.
from clio.exceptions import ClioError
try: # SDK operations await monitor.start_run(context, "Test")except ClioError as e: print(f"Clio SDK error: {e}")All Clio exceptions inherit from this class, so you can catch any SDK-related error by catching ClioError.
Specific Exceptions
Section titled “Specific Exceptions”ClioAuthError
Section titled “ClioAuthError”Raised when authentication fails or API key issues occur.
Common causes:
- Invalid API key format
- Expired or revoked API key
- Server returns 401 Unauthorized
- Network issues preventing authentication
from clio.exceptions import ClioAuthError
try: monitor = ClioMonitor(api_key="invalid_key") await monitor.start_run(context, "Test")except ClioAuthError as e: print(f"Authentication failed: {e}") # Handle by checking API key, regenerating if neededExample scenarios:
# Invalid API key formatClioAuthError("Invalid API key format")
# Server authentication failureClioAuthError("Invalid API key")
# Network/server issuesClioAuthError("Authentication server unavailable")ClioUploadError
Section titled “ClioUploadError”Raised when file upload operations fail.
Common causes:
- Network connectivity issues
- File not found or permissions issues
- Upload timeout (large files)
- S3 storage issues
- Invalid presigned URLs
from clio.exceptions import ClioUploadError
try: await monitor.start_run(context, "Test") # ... automation code ... await context.close() # Upload happens hereexcept ClioUploadError as e: print(f"Upload failed: {e}") # Handle by retrying or checking network connectivityExample scenarios:
# File not foundClioUploadError("File not found: /path/to/video.webm")
# Network timeoutClioUploadError("Upload timed out for large_video.webm")
# Permission issuesClioUploadError("Failed to upload trace.zip: Permission denied")
# S3 issuesClioUploadError("Upload failed: Invalid presigned URL")ClioRateLimitError
Section titled “ClioRateLimitError”Raised when API rate limits are exceeded.
Common causes:
- Monthly upload quota exceeded
- Too many concurrent requests
- Server-side rate limiting
- Bulk upload operations
from clio.exceptions import ClioRateLimitError
try: await monitor.start_run(context, "Test")except ClioRateLimitError as e: print(f"Rate limit exceeded: {e}") # Handle by waiting or upgrading planExample scenarios:
# Monthly quota exceededClioRateLimitError("Monthly rate limit exceeded")
# Too many requestsClioRateLimitError("Rate limit exceeded, try again later")
# Concurrent upload limitsClioRateLimitError("Too many concurrent uploads")ClioConfigError
Section titled “ClioConfigError”Raised when configuration validation fails.
Common causes:
- Invalid configuration parameters
- Missing required settings
- Conflicting configuration options
- Invalid URL formats
from clio.exceptions import ClioConfigError
try: config = Config( api_key="clio_abc123", base_url="invalid-url" )except ClioConfigError as e: print(f"Configuration error: {e}") # Handle by fixing configurationExample scenarios:
# Invalid URL formatClioConfigError("Invalid base_url format: not-a-url")
# Missing API keyClioConfigError("API key is required")
# Invalid parameter valuesClioConfigError("retry_attempts must be between 1 and 10")Error Handling Strategies
Section titled “Error Handling Strategies”Comprehensive Error Handling
Section titled “Comprehensive Error Handling”Handle all possible Clio errors:
from clio import ClioMonitorfrom clio.exceptions import ( ClioError, ClioAuthError, ClioUploadError, ClioRateLimitError, ClioConfigError)
async def monitored_automation(): try: monitor = ClioMonitor(api_key=os.getenv("CLIO_API_KEY"))
async with async_playwright() as p: browser = await p.chromium.launch() context = await browser.new_context(record_video_dir="./videos")
await monitor.start_run(context, "E2E Test")
# Your automation code here page = await context.new_page() await page.goto("https://example.com")
await context.close()
except ClioAuthError as e: print(f"❌ Authentication failed: {e}") print("💡 Check your API key and try again") return False
except ClioRateLimitError as e: print(f"⏳ Rate limit exceeded: {e}") print("💡 Wait a few minutes or upgrade your plan") return False
except ClioUploadError as e: print(f"📤 Upload failed: {e}") print("💡 Check your network connection and retry") return False
except ClioConfigError as e: print(f"⚙️ Configuration error: {e}") print("💡 Fix your configuration and try again") return False
except ClioError as e: print(f"❌ Unexpected Clio error: {e}") print("💡 Contact support if this persists") return False
except Exception as e: print(f"💥 Unexpected error: {e}") return False
print("✅ Automation completed successfully") return TrueGraceful Degradation
Section titled “Graceful Degradation”Continue testing even if monitoring fails:
async def robust_automation(): monitor = None
try: monitor = ClioMonitor(api_key=os.getenv("CLIO_API_KEY")) except ClioError as e: print(f"⚠️ Monitoring unavailable: {e}") print("🔄 Continuing without monitoring...")
async with async_playwright() as p: browser = await p.chromium.launch() context = await browser.new_context(record_video_dir="./videos")
# Try to start monitoring, but continue if it fails if monitor: try: await monitor.start_run(context, "Resilient Test") print("📹 Monitoring enabled") except ClioError as e: print(f"⚠️ Monitoring failed: {e}") print("🔄 Continuing without monitoring...")
# Run your test regardless page = await context.new_page() await page.goto("https://example.com")
await context.close() print("✅ Test completed")Retry Logic
Section titled “Retry Logic”Implement custom retry logic for transient errors:
import asynciofrom clio.exceptions import ClioUploadError, ClioRateLimitError
async def automation_with_retries(max_retries=3): for attempt in range(max_retries): try: monitor = ClioMonitor(api_key=os.getenv("CLIO_API_KEY"))
async with async_playwright() as p: browser = await p.chromium.launch() context = await browser.new_context(record_video_dir="./videos")
await monitor.start_run(context, f"Test Attempt {attempt + 1}")
# Your automation code page = await context.new_page() await page.goto("https://example.com")
await context.close()
print("✅ Success!") return True
except (ClioUploadError, ClioRateLimitError) as e: print(f"⚠️ Attempt {attempt + 1} failed: {e}")
if attempt < max_retries - 1: wait_time = 2 ** attempt # Exponential backoff print(f"⏳ Waiting {wait_time} seconds before retry...") await asyncio.sleep(wait_time) else: print("❌ All retry attempts failed") return False
except ClioError as e: print(f"❌ Non-retryable error: {e}") return False
return FalseConfiguration-Based Error Handling
Section titled “Configuration-Based Error Handling”Development Mode
Section titled “Development Mode”Use raise_on_error=True during development to get detailed error information:
# Development configuration - raises exceptionsmonitor = ClioMonitor( api_key="clio_abc123", raise_on_error=True # Get full exception details)
try: await monitor.start_run(context, "Debug Test")except ClioError as e: # Full exception with stack trace import traceback traceback.print_exc() print(f"Detailed error: {e}")Production Mode
Section titled “Production Mode”Use raise_on_error=False in production for graceful error handling:
# Production configuration - logs errors but continuesmonitor = ClioMonitor( api_key="clio_abc123", raise_on_error=False # Log errors, don't raise exceptions)
# Errors are logged but don't interrupt your automationawait monitor.start_run(context, "Production Test")# Continues even if monitoring failsLogging Integration
Section titled “Logging Integration”Combine exception handling with proper logging:
import loggingfrom clio.exceptions import ClioError
# Configure logginglogging.basicConfig(level=logging.INFO)logger = logging.getLogger(__name__)
async def logged_automation(): try: monitor = ClioMonitor(api_key=os.getenv("CLIO_API_KEY")) logger.info("🎬 Starting monitored automation")
# ... automation code ...
logger.info("✅ Automation completed successfully")
except ClioAuthError as e: logger.error(f"Authentication failed: {e}") # Send alert to monitoring system
except ClioUploadError as e: logger.warning(f"Upload failed: {e}") # Continue with test, upload failure not critical
except ClioError as e: logger.error(f"Clio SDK error: {e}") # Log for debugging but don't fail the testBest Practices
Section titled “Best Practices”-
Catch specific exceptions rather than generic
Exception:# Goodexcept ClioAuthError as e:handle_auth_error(e)# Avoidexcept Exception as e:# Too broad, might catch unrelated errors -
Use appropriate error handling for your context:
# Development - want to see all errorsmonitor = ClioMonitor(api_key="...", raise_on_error=True)# Production - graceful degradationmonitor = ClioMonitor(api_key="...", raise_on_error=False) -
Implement retry logic for transient errors:
# Retry upload errors and rate limitsexcept (ClioUploadError, ClioRateLimitError):# Implement exponential backoff -
Log errors appropriately:
# Include context but not sensitive datalogger.error(f"Upload failed for run {run_name}: {e}") -
Provide user-friendly error messages:
except ClioAuthError:print("❌ Authentication failed. Please check your API key.")