Skip to content
Get started
SDKs
Python

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.

TabstackError (base class)
├── APIError
│ ├── APIConnectionError
│ │ └── APITimeoutError
│ ├── APIResponseValidationError
│ └── APIStatusError
│ ├── BadRequestError (400)
│ ├── AuthenticationError (401)
│ ├── PermissionDeniedError (403)
│ ├── NotFoundError (404)
│ ├── ConflictError (409)
│ ├── UnprocessableEntityError (422)
│ ├── RateLimitError (429)
│ └── InternalServerError (>=500)
import tabstack
from tabstack import (
Tabstack,
TabstackError,
APIError,
APIStatusError,
APIConnectionError,
APITimeoutError,
APIResponseValidationError,
BadRequestError,
AuthenticationError,
PermissionDeniedError,
NotFoundError,
ConflictError,
UnprocessableEntityError,
RateLimitError,
InternalServerError,
)

Base error class for all SDK errors.

Base class for all API-related errors.

Raised when the SDK cannot connect to the API.

Common causes:

  • Network connectivity issues
  • DNS resolution failures
  • Firewall blocking requests

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

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

Raised when the request is malformed or invalid.

Common causes:

  • Missing required parameters
  • Invalid JSON schema format
  • Malformed request body

Raised when authentication fails.

Common causes:

  • Invalid API key
  • Missing API key
  • Expired API key

Raised when access to a resource is forbidden.

Common causes:

  • Insufficient permissions
  • Resource access denied

Raised when the requested resource is not found.

Common causes:

  • Invalid endpoint URL
  • Resource no longer exists

Raised when there’s a conflict with the current state.

Common causes:

  • Resource already exists
  • Concurrent modification conflict

Raised when the request is valid but cannot be processed.

Common causes:

  • Invalid URL format
  • URL points to private/internal resources
  • Semantic validation errors

Raised when rate limits are exceeded.

Common causes:

  • Too many requests in a short period
  • API quota exceeded

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)
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}')
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

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}')
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}")
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')

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'
)
# Good
try:
result = client.extract.markdown(url=url)
except tabstack.TabstackError as error:
# Handle error
pass
# 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
# 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
# Good: Automatic cleanup even on errors
with Tabstack(api_key=api_key) as client:
result = client.extract.markdown(url=url)
# 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)
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')
import tabstack
try:
result = client.extract.markdown(url='https://localhost:3000')
except tabstack.UnprocessableEntityError:
print('Cannot access private URLs like localhost')
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')
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