Skip to main content

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 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