Skip to main content

Error Handling

The Meter SDK uses exceptions to signal errors. All API-related errors raise MeterError, which you should catch and handle appropriately.

MeterError

The base exception for all Meter SDK errors.
from meter_sdk import MeterError

class MeterError(Exception):
    """Base exception for Meter SDK errors"""
    pass

Catching errors

from meter_sdk import MeterClient, MeterError

client = MeterClient(api_key="sk_live_...")

try:
    strategy = client.generate_strategy(
        url="https://example.com",
        description="Extract data",
        name="Test Strategy"
    )
except MeterError as e:
    print(f"API error: {e}")
    # Handle error appropriately

Common error scenarios

401 Unauthorized

Cause: Invalid or missing API key
try:
    client = MeterClient(api_key="invalid_key")
    strategies = client.list_strategies()
except MeterError as e:
    # "Invalid or missing API key"
    print(f"Authentication failed: {e}")
Solutions:
  • Verify your API key is correct
  • Check that the key hasn’t been deleted
  • Ensure you’re using the full key (starts with sk_live_)

400 Bad Request

Cause: Invalid request parameters
try:
    # Missing required parameter
    strategy = client.generate_strategy(
        url="https://example.com",
        description="",  # Empty description
        name="Test"
    )
except MeterError as e:
    # "Invalid request parameters"
    print(f"Validation error: {e}")
Solutions:
  • Check all required parameters are provided
  • Verify parameter types are correct
  • Ensure values are valid (e.g., URL is well-formed)

404 Not Found

Cause: Resource doesn’t exist
try:
    strategy = client.get_strategy("invalid-uuid")
except MeterError as e:
    # "Strategy not found"
    print(f"Resource not found: {e}")
Solutions:
  • Verify the UUID is correct
  • Check the resource hasn’t been deleted
  • Ensure you have permission to access the resource

429 Too Many Requests

Cause: Rate limit exceeded on strategy generation
try:
    strategy = client.generate_strategy(url, description, name)
except MeterError as e:
    # "Please slow down - limits on strategy generation"
    print(f"Rate limited: {e}")
Solutions:
  • Wait before retrying (the API returns a Retry-After header with seconds to wait)
  • Reduce request frequency
  • Use exponential backoff

500 Internal Server Error

Cause: Server-side error
try:
    job = client.create_job(strategy_id, url)
except MeterError as e:
    # "Internal server error"
    print(f"Server error: {e}")
Solutions:
  • Retry the request after a short delay
  • If persistent, contact support
  • Check API status page for incidents

503 Service Unavailable

Cause: LLM service temporarily unavailable
try:
    strategy = client.generate_strategy(url, description, name)
except MeterError as e:
    # "Service temporarily unavailable"
    print(f"Service unavailable: {e}")
Solutions:
  • Retry the request after 30-60 seconds
  • This is usually temporary

Timeout errors

When waiting for jobs with a timeout:
from meter_sdk import MeterError

try:
    completed = client.wait_for_job(
        job_id,
        timeout=60.0  # 1 minute
    )
except MeterError as e:
    # "Job timed out after 60 seconds"
    print(f"Timeout: {e}")

    # Check job status manually
    job = client.get_job(job_id)
    if job['status'] == 'failed':
        print(f"Job failed: {job['error']}")
    else:
        print(f"Job still {job['status']}, wait longer")

Best practices

Always catch MeterError

from meter_sdk import MeterClient, MeterError

client = MeterClient(api_key="sk_live_...")

try:
    # API calls
    strategy = client.generate_strategy(url, description, name)
    job = client.create_job(strategy['strategy_id'], url)
    result = client.wait_for_job(job['job_id'])

except MeterError as e:
    # Log error
    logger.error(f"Meter API error: {e}")

    # Handle appropriately
    send_alert(f"Scraping failed: {e}")

Implement retry logic

For transient errors (429, 500, 503, network issues):
import time
from meter_sdk import MeterError

def generate_strategy_with_retry(client, url, description, name, max_retries=3):
    """Generate strategy with retry logic"""
    for attempt in range(max_retries):
        try:
            return client.generate_strategy(url, description, name)
        except MeterError as e:
            error_str = str(e).lower()

            # Rate limited - wait longer (respect Retry-After)
            if "429" in str(e) or "slow down" in error_str:
                wait_time = 60  # Default from Retry-After header
                print(f"Rate limited. Waiting {wait_time}s...")
                time.sleep(wait_time)
                continue

            # Server errors - exponential backoff
            if "500" in str(e) or "503" in str(e) or "unavailable" in error_str:
                if attempt < max_retries - 1:
                    wait_time = 2 ** attempt
                    print(f"Retry {attempt + 1}/{max_retries} in {wait_time}s")
                    time.sleep(wait_time)
                else:
                    raise  # Max retries exceeded
            else:
                raise  # Non-retryable error

Graceful degradation

Handle errors without crashing:
from meter_sdk import MeterError

def fetch_latest_data(client, schedule_id):
    """Fetch changes with fallback"""
    try:
        changes = client.get_schedule_changes(schedule_id)
        return changes['changes']

    except MeterError as e:
        logger.warning(f"Failed to fetch changes: {e}")

        # Fallback: use cached data
        return load_from_cache(schedule_id)

Log errors for debugging

import logging
from meter_sdk import MeterError

logger = logging.getLogger(__name__)

try:
    strategy = client.generate_strategy(url, description, name)
except MeterError as e:
    # Log with context
    logger.error(
        "Strategy generation failed",
        extra={
            "url": url,
            "description": description,
            "error": str(e)
        }
    )
    raise

Error response format

API errors return JSON with details:
{
  "detail": "Error message describing what went wrong"
}
The SDK extracts this message and includes it in the MeterError exception.

Handling specific error types

Strategy generation failures

try:
    strategy = client.generate_strategy(url, description, name)
except MeterError as e:
    if "not accessible" in str(e).lower():
        print("URL cannot be accessed. Check if it's public.")
    elif "invalid url" in str(e).lower():
        print("URL format is invalid")
    else:
        print(f"Generation failed: {e}")

Job failures

job = client.get_job(job_id)

if job['status'] == 'failed':
    error = job['error']

    if "selector not found" in error.lower():
        print("Website structure changed. Regenerate strategy.")
    elif "timeout" in error.lower():
        print("Website took too long to respond")
    else:
        print(f"Job failed: {error}")

Error monitoring

Set up error tracking for production:
import sentry_sdk
from meter_sdk import MeterError

sentry_sdk.init(dsn="your-sentry-dsn")

try:
    strategy = client.generate_strategy(url, description, name)
except MeterError as e:
    # Automatically captured by Sentry
    sentry_sdk.capture_exception(e)
    raise

Next steps

MeterClient Reference

Explore all SDK methods

Quick Start

See error handling in practice

Best Practices

Learn SDK best practices

REST API Errors

View REST API error codes

Need help?

Email me at mckinnon@meter.sh