Error Handling
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 codemessage: str- Error messagebody: 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: Get started with the SDK
- Generate Features: Discover AI transformations
- Automate Features: Execute browser automation