Exception Handling
Exception handling is the mechanism of responding to anomalies (exceptions) during execution. In Python, it is done via `try`, `except`, `else`, `finally`, and ...
Definition
Exception handling is the mechanism of responding to anomalies (exceptions) during execution. In Python, it is done via try, except, else, finally, and customized raise declarations.
Why It Exists
Third-party APIs fail regularly due to rate limiting (429), timeouts, network interruptions, or raw service crashes. A production AI backend must intercept these exceptions gracefully, log them, retry the execution, or switch to backup providers instead of crashing.
How It Works
EXCEPTION PIPELINE
┌────────────────┐
│ Try block code │ (Execute request)
└───────┬────────┘
│
┌────────────────┴────────────────┐
[Exception Raised?] [No Error Raised]
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ Except Block│ (Log/Recover) │ Else Block │ (Proceed)
└──────┬──────┘ └──────┬──────┘
│ │
└───────────────┬─────────────────┘
▼
┌───────────────┐
│ Finally Block │ (Clean up resources/Close files)
└───────────────┘
1. **Catching Specific Errors**: Never use a bare `except:`. This catches system exits (`KeyboardInterrupt`), which makes terminating the app impossible.
2. **Finally Block Execution**: The `finally` block runs regardless of whether an exception occurred, ensuring files, database connections, and HTTP sockets are closed safely.Real-World Example
In Cursor, if the primary backend fails to connect to Claude, the system catches the connection error and raises an informative popup notification in the editor asking you to check your network connection, rather than freezing the editing workspace.
Python Example
Here is a production pattern for safe third-party API calling with structured error logging and recovery:
import logging
class LLMAPIError(Exception):
"""Base exception for all LLM API operations."""
pass
class RateLimitError(LLMAPIError):
"""Exception raised when API returns 429 status."""
pass
def call_mock_gateway(url: str, request_data: dict) -> dict:
# Represents mock network gateway check
if not url:
raise ConnectionRefusedError("Unable to locate endpoint target.")
if "data" not in request_data:
raise ValueError("Missing 'data' key payload.")
return {"status": "success", "tokens": 120}
def safe_api_runner(url: str, data: dict) -> dict:
"""Wrapper that catches low-level exceptions and raises custom domain errors."""
try:
response = call_mock_gateway(url, data)
except ConnectionRefusedError as e:
logging.error(f"Network failure calling endpoint {url}: {e}")
raise LLMAPIError("Internal model connection failed.") from e
except ValueError as e:
logging.error(f"Invalid payload config: {e}")
raise LLMAPIError("Corrupt request format.") from e
except Exception as e:
logging.error(f"Unexpected error: {e}")
raise LLMAPIError("An unknown system error occurred.") from e
else:
logging.info("Request completed successfully.")
return response
finally:
logging.info("Cleaning up open connection channels.")
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
try:
safe_api_runner("http://api.model.com", {}) # Trigger ValueError
except LLMAPIError as e:
print(f"Intercepted Custom Exception: {e}")Interview Questions
- Beginner: Why is using a bare
except:generally considered a bad practice? - Intermediate: What is the difference between
except Exception as e:and custom user-defined exceptions? - Advanced: Explain the purpose of
raise ... from e(Exception Chaining).
Interview Answers
- Beginner: A bare
except:catches every exception, including system signals likeSystemExitorKeyboardInterrupt. This blocks the developer from terminating the running script via Ctrl+C in the terminal. - Intermediate:
except Exception as e:catches all standard Python runtime exceptions. Custom exceptions inherit fromExceptionand allow your application to categorize domain-specific failures (likeInsufficentTokenBalanceError), making exception handling more granular. - Advanced: Exception Chaining (
raise CustomError from e) preserves the stack trace of the original underlying exception (e) while raising a cleaner high-level error (CustomError). This helps developers trace the root cause during debugging.
Common Mistakes
- Swallowing exceptions: Catching an error and doing nothing:
except: pass. This hides bugs, making system failures impossible to debug. At a minimum, log the exception.
Assignment
Write a function that parses a string to float. If a ValueError is raised, return a default value of 0.0. If any other error occurs, re-raise it.