← Roadmap 🐍 Month 1: Python
0/10

Error Handling

LLM APIs fail constantly. Networks timeout. Files are missing. Your code MUST handle errors gracefully — or it will crash in production.

Learning Objectives

try/except — The Basics

python
# Basic pattern
try:
    result = 10 / 0
except ZeroDivisionError:
    print("Can't divide by zero!")

# Full pattern with else and finally
try:
    data = json.loads(response_text)
except json.JSONDecodeError as e:
    print(f"Invalid JSON: {e}")
except KeyError as e:
    print(f"Missing key: {e}")
else:
    # Runs only if NO exception occurred
    print(f"Parsed successfully: {data}")
finally:
    # Always runs, exception or not
    print("Cleanup complete")

Catch Specific Exceptions — Never Bare except!

python
# ❌ BAD — catches EVERYTHING including KeyboardInterrupt
try:
    call_api()
except:
    print("Something went wrong")  # hides real bugs!

# ✅ GOOD — catch specific errors
try:
    call_api()
except requests.Timeout:
    print("Request timed out")
except requests.ConnectionError:
    print("Can't reach the server")
except requests.HTTPError as e:
    print(f"HTTP error: {e.response.status_code}")

# ✅ Catch multiple in one line
except (ValueError, TypeError) as e:
    print(f"Invalid input: {e}")

Raising Exceptions

python
def call_llm(prompt: str, api_key: str) -> dict:
    """Call LLM with proper validation."""
    if not prompt.strip():
        raise ValueError("Prompt cannot be empty")
    if not api_key:
        raise ValueError("API key is required")

    # Re-raise with more context
    try:
        response = requests.post(url, json={"prompt": prompt}, timeout=30)
        response.raise_for_status()
        return response.json()
    except requests.HTTPError as e:
        raise RuntimeError(f"LLM API failed with status {e.response.status_code}") from e

Custom Exception Classes

python
class LLMAPIError(Exception):
    """Base exception for LLM API errors."""
    pass

class RateLimitError(LLMAPIError):
    """Raised when API rate limit is hit."""
    def __init__(self, retry_after: int = 60):
        self.retry_after = retry_after
        super().__init__(f"Rate limited. Retry after {retry_after}s")

class TokenLimitError(LLMAPIError):
    """Raised when prompt exceeds token limit."""
    def __init__(self, tokens: int, limit: int):
        self.tokens = tokens
        self.limit = limit
        super().__init__(f"Prompt has {tokens} tokens (limit: {limit})")

# Usage
try:
    result = call_llm(long_prompt)
except RateLimitError as e:
    print(f"Waiting {e.retry_after}s before retry...")
except TokenLimitError as e:
    print(f"Trim prompt from {e.tokens} to {e.limit} tokens")

Logging Instead of Print

python
import logging

# Configure logging (do this once at app startup)
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s"
)
logger = logging.getLogger("chat-api")

# Use it
logger.info("Starting API server on port 8000")
logger.warning("Rate limit approaching: 58/60 requests")
logger.error("Failed to call OpenAI API: timeout after 30s")
logger.debug("Request payload: %s", payload)  # only shown at DEBUG level

# In error handling
try:
    result = call_llm(prompt)
except requests.Timeout:
    logger.error("LLM API timeout for prompt: %s", prompt[:50])
    raise LLMAPIError("Request timed out")

Common API Errors Reference

text
Error                   When                    What to do
────────────────────── ─────────────────────── ──────────────────
requests.Timeout       API takes too long      Retry with backoff
requests.ConnectionError  No internet / DNS     Check network
requests.HTTPError(401)  Invalid API key       Check .env file
requests.HTTPError(429)  Rate limited          Wait and retry
requests.HTTPError(500)  Server crash           Retry once
json.JSONDecodeError    Bad JSON in response   Log and handle
FileNotFoundError       Config file missing     Create default
KeyError                Missing dict key        Use .get() with default
ValueError              Wrong data type         Validate input first
📝

Exercise — Resilient API Caller

Write a function safe_api_call(url, max_retries=3) that handles Timeout, ConnectionError, HTTP 429 (wait+retry), HTTP 5xx (retry), and HTTP 4xx (raise). Use logging instead of print. Test it against httpbin.org/status/429 and httpbin.org/delay/10.

✅ You've completed this step when you can confirm: