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
- Use try/except/else/finally blocks
- Catch specific exception types (not bare except)
- Raise your own exceptions with clear messages
- Create custom exception classes
- Use the logging module instead of print
- Handle the common errors you'll hit with APIs
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 eCustom 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.