Error Handling
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
Section titled “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
Section titled “Importing Error Classes”import tabstackfrom tabstack import ( Tabstack, TabstackError, APIError, APIStatusError, APIConnectionError, APITimeoutError, APIResponseValidationError, BadRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, UnprocessableEntityError, RateLimitError, InternalServerError,)Error Classes Reference
Section titled “Error Classes Reference”TabstackError
Section titled “TabstackError”Base error class for all SDK errors.
APIError
Section titled “APIError”Base class for all API-related errors.
APIConnectionError
Section titled “APIConnectionError”Raised when the SDK cannot connect to the API.
Common causes:
- Network connectivity issues
- DNS resolution failures
- Firewall blocking requests
APITimeoutError
Section titled “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
Section titled “APIStatusError”Base class for HTTP status code errors. All status errors have these attributes:
status_code: int- HTTP status codemessage: str- Error messagebody: object- Response body
BadRequestError (400)
Section titled “BadRequestError (400)”Raised when the request is malformed or invalid.
Common causes:
- Missing required parameters
- Invalid JSON schema format
- Malformed request body
AuthenticationError (401)
Section titled “AuthenticationError (401)”Raised when authentication fails.
Common causes:
- Invalid API key
- Missing API key
- Expired API key
PermissionDeniedError (403)
Section titled “PermissionDeniedError (403)”Raised when access to a resource is forbidden.
Common causes:
- Insufficient permissions
- Resource access denied
NotFoundError (404)
Section titled “NotFoundError (404)”Raised when the requested resource is not found.
Common causes:
- Invalid endpoint URL
- Resource no longer exists
ConflictError (409)
Section titled “ConflictError (409)”Raised when there’s a conflict with the current state.
Common causes:
- Resource already exists
- Concurrent modification conflict
UnprocessableEntityError (422)
Section titled “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)
Section titled “RateLimitError (429)”Raised when rate limits are exceeded.
Common causes:
- Too many requests in a short period
- API quota exceeded
InternalServerError (>=500)
Section titled “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
Section titled “Basic Error Handling”Simple Try-Except
Section titled “Simple Try-Except”import osfrom 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
Section titled “Handling Specific Error Types”import osimport tabstackfrom 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}') raiseAdvanced Error Handling
Section titled “Advanced Error Handling”Retry Logic with Exponential Backoff
Section titled “Retry Logic with Exponential Backoff”The SDK has built-in retry logic (default: 2 retries), but you can implement custom retry behavior:
import osimport timeimport tabstackfrom 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}")
# Usagetry: 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
Section titled “Batch Processing with Error Tracking”import osfrom typing import List, Anyfrom dataclasses import dataclassimport tabstackfrom tabstack import Tabstack
@dataclassclass 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
# Usageurls = [ '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
Section titled “Error Logging”import osimport loggingimport tabstackfrom tabstack import Tabstack
# Configure logginglogging.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
# Usagelogged_extract('https://example.com')Automate Error Handling
Section titled “Automate Error Handling”The automate endpoint streams events, so error handling works differently:
import osimport tabstackfrom 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
# Usageautomate_with_error_handling( 'Extract the top 5 articles', 'https://news.example.com')Best Practices
Section titled “Best Practices”1. Always Use Try-Except
Section titled “1. Always Use Try-Except”# Goodtry: result = client.extract.markdown(url=url)except tabstack.TabstackError as error: # Handle error pass2. Handle Specific Errors First
Section titled “2. Handle Specific Errors First”# Good: Specific to generictry: result = client.extract.markdown(url=url)except tabstack.AuthenticationError: # Handle auth error - don't retry passexcept tabstack.RateLimitError: # Handle rate limit - wait and retry passexcept tabstack.UnprocessableEntityError: # Handle invalid URL - check the URL passexcept tabstack.InternalServerError: # Handle server error - maybe retry passexcept tabstack.APIStatusError: # Handle other API errors passexcept tabstack.APIConnectionError: # Handle connection issues passexcept Exception: # Handle unexpected errors pass3. Implement Retry for Transient Errors
Section titled “3. Implement Retry for Transient Errors”# Good: Retry server errors and rate limitstry: result = client.extract.markdown(url=url)except (tabstack.InternalServerError, tabstack.RateLimitError): # Retry with backoff passexcept tabstack.AuthenticationError: # Don't retry - fix the API key raise4. Use Context Managers
Section titled “4. Use Context Managers”# Good: Automatic cleanup even on errorswith Tabstack(api_key=api_key) as client: result = client.extract.markdown(url=url)5. Configure Retries at Client Level
Section titled “5. Configure Retries at Client Level”# Disable retries for faster failureclient = Tabstack(max_retries=0)
# Increase retries for important requestsclient = Tabstack(max_retries=5)
# Per-request retry overrideresult = client.with_options(max_retries=5).extract.markdown(url=url)Common Error Scenarios
Section titled “Common Error Scenarios”Invalid API Key
Section titled “Invalid API Key”import tabstackfrom 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
Section titled “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
Section titled “Request Timeout”import tabstackfrom 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
Section titled “Rate Limiting”import tabstackimport 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 retryingNext Steps
Section titled “Next Steps”- Quickstart: Get started with the SDK
- Generate Features: Discover AI transformations
- Automate Features: Execute browser automation