--- title: Error Handling | Tabstack description: Build robust Python applications with the Tabstack SDK's comprehensive exception hierarchy and handling patterns. --- Building robust applications requires proper error handling. The Tabstack Python SDK provides specific exception classes for different error scenarios. ## Error Class Hierarchy ``` TabstackError (base class) ├── APIError │ ├── APIConnectionError │ │ └── APITimeoutError │ ├── APIResponseValidationError │ └── APIStatusError │ ├── BadRequestError (400) │ ├── AuthenticationError (401) │ ├── PermissionDeniedError (403) │ ├── NotFoundError (404) │ ├── ConflictError (409) │ ├── UnprocessableEntityError (422) │ ├── RateLimitError (429) │ └── InternalServerError (>=500) ``` ## Importing Error Classes ``` import tabstack from tabstack import ( Tabstack, TabstackError, APIError, APIStatusError, APIConnectionError, APITimeoutError, APIResponseValidationError, BadRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, UnprocessableEntityError, RateLimitError, InternalServerError, ) ``` ## Error Classes Reference ### TabstackError Base error class for all SDK errors. ### APIError Base class for all API-related errors. ### APIConnectionError Raised when the SDK cannot connect to the API. **Common causes:** - Network connectivity issues - DNS resolution failures - Firewall blocking requests ### APITimeoutError A subclass of `APIConnectionError` raised when a request times out. **Common causes:** - Server taking too long to respond - Network latency issues - Request timeout set too low ### APIStatusError Base class for HTTP status code errors. All status errors have these attributes: - `status_code: int` - HTTP status code - `message: str` - Error message - `body: object` - Response body ### BadRequestError (400) Raised when the request is malformed or invalid. **Common causes:** - Missing required parameters - Invalid JSON schema format - Malformed request body ### AuthenticationError (401) Raised when authentication fails. **Common causes:** - Invalid API key - Missing API key - Expired API key ### PermissionDeniedError (403) Raised when access to a resource is forbidden. **Common causes:** - Insufficient permissions - Resource access denied ### NotFoundError (404) Raised when the requested resource is not found. **Common causes:** - Invalid endpoint URL - Resource no longer exists ### ConflictError (409) Raised when there’s a conflict with the current state. **Common causes:** - Resource already exists - Concurrent modification conflict ### UnprocessableEntityError (422) Raised when the request is valid but cannot be processed. **Common causes:** - Invalid URL format - URL points to private/internal resources - Semantic validation errors ### RateLimitError (429) Raised when rate limits are exceeded. **Common causes:** - Too many requests in a short period - API quota exceeded ### InternalServerError (>=500) Raised when the server encounters an internal error. **Common causes:** - Failed to fetch the target URL - Page content too large - Failed to generate/extract data - Service temporarily unavailable (503) ## Basic Error Handling ### Simple Try-Except ``` import os from tabstack import Tabstack, TabstackError with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client: try: result = client.extract.markdown(url='https://example.com') print(result.content) except TabstackError as error: print(f'API error: {error}') except Exception as error: print(f'Unexpected error: {error}') ``` ### Handling Specific Error Types ``` import os import tabstack from tabstack import Tabstack with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client: try: result = client.extract.markdown(url='https://example.com') print(result.content) except tabstack.AuthenticationError: print('Authentication failed. Please check your API key.') except tabstack.UnprocessableEntityError as error: print(f'Invalid or inaccessible URL: {error}') except tabstack.RateLimitError as error: print(f'Rate limited. Status: {error.status_code}') except tabstack.InternalServerError as error: print(f'Server error occurred: {error}') except tabstack.APIConnectionError as error: print(f'Connection error: {error.__cause__}') except tabstack.APIStatusError as error: print(f'API error {error.status_code}: {error.message}') except Exception as error: print(f'Unexpected error: {error}') raise ``` ## Advanced Error Handling ### Retry Logic with Exponential Backoff The SDK has built-in retry logic (default: 2 retries), but you can implement custom retry behavior: ``` import os import time import tabstack from tabstack import Tabstack def extract_with_retry(url: str, max_retries: int = 3, base_delay: float = 1.0): """Extract markdown with custom retry logic for server errors.""" last_error = None for attempt in range(max_retries): try: with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client: result = client.extract.markdown(url=url) return result except (tabstack.InternalServerError, tabstack.RateLimitError) as error: last_error = error if attempt < max_retries - 1: # Exponential backoff: 1s, 2s, 4s, etc. delay = base_delay * (2 ** attempt) print(f"Attempt {attempt + 1} failed. Retrying in {delay}s...") time.sleep(delay) continue except tabstack.APIStatusError as error: # Don't retry client errors (4xx except 429) raise raise Exception(f"Failed after {max_retries} attempts: {last_error}") # Usage try: result = extract_with_retry('https://example.com') print('Success:', result.content[:100]) except Exception as error: print(f'Failed: {error}') ``` ### Batch Processing with Error Tracking ``` import os from typing import List, Any from dataclasses import dataclass import tabstack from tabstack import Tabstack @dataclass class BatchResult: url: str success: bool data: Any = None error: str = None def batch_extract_with_error_handling( urls: List[str], schema: dict ) -> List[BatchResult]: """Extract from multiple URLs with error tracking.""" results = [] with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client: for url in urls: try: result = client.extract.json(url=url, json_schema=schema) results.append(BatchResult( url=url, success=True, data=result )) print(f"Success: {url}") except tabstack.APIStatusError as error: results.append(BatchResult( url=url, success=False, error=f"{error.status_code}: {error.message}" )) print(f"Failed: {url} - {error.status_code}: {error.message}") except Exception as error: results.append(BatchResult( url=url, success=False, error=str(error) )) print(f"Failed: {url} - {error}") return results # Usage urls = [ 'https://example.com/page1', 'https://example.com/page2', 'https://example.com/page3' ] schema = {"type": "object", "properties": {"title": {"type": "string"}}} results = batch_extract_with_error_handling(urls, schema) successful = [r for r in results if r.success] failed = [r for r in results if not r.success] print(f"\nBatch Complete: {len(successful)} succeeded, {len(failed)} failed") if failed: print('\nFailed URLs:') for result in failed: print(f" - {result.url}: {result.error}") ``` ### Error Logging ``` import os import logging import tabstack from tabstack import Tabstack # Configure logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger('tabstack_app') def logged_extract(url: str): """Extract with comprehensive error logging.""" with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client: try: logger.info(f"Starting extraction from {url}") result = client.extract.markdown(url=url) logger.info(f"Successfully extracted {len(result.content)} characters") return result except tabstack.APIStatusError as error: logger.error( f"API error during extraction", extra={ 'url': url, 'error_type': type(error).__name__, 'status_code': error.status_code, 'message': str(error) } ) raise except tabstack.APIConnectionError as error: logger.error(f"Connection error during extraction from {url}: {error}") raise except Exception as error: logger.exception(f"Unexpected error during extraction from {url}") raise # Usage logged_extract('https://example.com') ``` ## Automate Error Handling The automate endpoint streams events, so error handling works differently: ``` import os import tabstack from tabstack import Tabstack def automate_with_error_handling(task: str, url: str): """Execute automation with proper error handling.""" with Tabstack(api_key=os.getenv('TABSTACK_API_KEY')) as client: try: stream = client.agent.automate(task=task, url=url) for event in stream: # Check event type for errors if event.event == 'error': error_data = event.data print(f'Automation error: {error_data}') raise Exception(f'Automation failed: {error_data}') elif event.event == 'task:aborted': reason = event.data print(f'Task aborted: {reason}') raise Exception(f'Task aborted: {reason}') elif event.event == 'complete': result = event.data print(f'Success: {result}') return result except tabstack.APIStatusError as error: print(f'API error during automation: {error.status_code} - {error}') raise except tabstack.APIConnectionError as error: print(f'Connection error: {error}') raise except Exception as error: print(f'Unexpected error: {error}') raise # Usage automate_with_error_handling( 'Extract the top 5 articles', 'https://news.example.com' ) ``` ## Best Practices ### 1. Always Use Try-Except ``` # Good try: result = client.extract.markdown(url=url) except tabstack.TabstackError as error: # Handle error pass ``` ### 2. Handle Specific Errors First ``` # Good: Specific to generic try: result = client.extract.markdown(url=url) except tabstack.AuthenticationError: # Handle auth error - don't retry pass except tabstack.RateLimitError: # Handle rate limit - wait and retry pass except tabstack.UnprocessableEntityError: # Handle invalid URL - check the URL pass except tabstack.InternalServerError: # Handle server error - maybe retry pass except tabstack.APIStatusError: # Handle other API errors pass except tabstack.APIConnectionError: # Handle connection issues pass except Exception: # Handle unexpected errors pass ``` ### 3. Implement Retry for Transient Errors ``` # Good: Retry server errors and rate limits try: result = client.extract.markdown(url=url) except (tabstack.InternalServerError, tabstack.RateLimitError): # Retry with backoff pass except tabstack.AuthenticationError: # Don't retry - fix the API key raise ``` ### 4. Use Context Managers ``` # Good: Automatic cleanup even on errors with Tabstack(api_key=api_key) as client: result = client.extract.markdown(url=url) ``` ### 5. Configure Retries at Client Level ``` # Disable retries for faster failure client = Tabstack(max_retries=0) # Increase retries for important requests client = Tabstack(max_retries=5) # Per-request retry override result = client.with_options(max_retries=5).extract.markdown(url=url) ``` ## Common Error Scenarios ### Invalid API Key ``` import tabstack from tabstack import Tabstack try: with Tabstack(api_key='invalid-key') as client: result = client.extract.markdown(url='https://example.com') except tabstack.AuthenticationError: print('Please set a valid API key in TABSTACK_API_KEY environment variable') ``` ### URL Cannot Be Accessed ``` import tabstack try: result = client.extract.markdown(url='https://localhost:3000') except tabstack.UnprocessableEntityError: print('Cannot access private URLs like localhost') ``` ### Request Timeout ``` import tabstack from tabstack import Tabstack try: # Set a short timeout with Tabstack(timeout=5.0) as client: result = client.extract.markdown(url='https://slow-site.example.com') except tabstack.APITimeoutError: print('Request timed out - try increasing the timeout') ``` ### Rate Limiting ``` import tabstack import time try: result = client.extract.markdown(url='https://example.com') except tabstack.RateLimitError as error: print(f'Rate limited (status {error.status_code}). Waiting before retry...') time.sleep(60) # Wait before retrying ``` ## Next Steps - **[Quickstart](./quickstart)**: Get started with the SDK - **[Generate Features](./generate)**: Discover AI transformations - **[Automate Features](./automate)**: Execute browser automation