Home› Tutorials› Error Handling
Error HandlingException Handling in Python: try, except, else, finally
- Python uses exceptions for error signalling; catching them lets you recover or report gracefully.
- Always catch specific exception types rather than bare
except:. - The
elseblock runs only when no exception occurred;finallyalways runs. - Define custom exception classes by inheriting from
Exception.
try and except
When Python encounters an error it cannot handle automatically, it raises an exception—an object that represents the error. If the exception is not caught, it propagates up the call stack until it either reaches a handler or terminates the program with a traceback.
A try/except block intercepts the exception before that happens:
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero")
result = None
The code in the try block runs normally. If any line raises ZeroDivisionError, Python jumps to the matching except clause. Lines after the error inside try are skipped.
Catching specific exception types
You can handle different error types with separate clauses and access the exception object using as:
def read_config(path):
try:
with open(path) as fh:
return fh.read()
except FileNotFoundError:
print(f"Config file not found: {path}")
return None
except PermissionError as exc:
print(f"Permission denied: {exc}")
return None
To catch multiple types in one clause, use a tuple:
except (ValueError, TypeError) as exc:
print(f"Bad input: {exc}")
Avoid catching all exceptions with a bare except: or except Exception: at the top level unless you immediately log and re-raise. Silently swallowing unexpected exceptions is one of the most common sources of mysterious bugs.
The else and finally clauses
A try block supports two optional additional clauses:
try:
data = json.loads(raw_text)
except json.JSONDecodeError as exc:
print(f"Invalid JSON: {exc}")
data = {}
else:
# Runs only if NO exception was raised
validate(data)
finally:
# Always runs, exception or not
cleanup()
else is useful when you want to run code that should only happen after a successful try, but that code itself might raise different exceptions you do not want the outer handler to catch.
finally runs regardless of what happened: success, caught exception, or even an uncaught exception bubbling past. It is the right place to release resources, close connections, or restore state.
Exception hierarchy
Python exceptions form an inheritance tree. Catching a parent class catches all its children. Key built-in types:
BaseException—the root.KeyboardInterruptandSystemExitlive here. Do not catch this unless you are writing a top-level runner.Exception—the base for almost all catchable errors.LookupError—parent ofKeyErrorandIndexError.OSError—parent ofFileNotFoundError,PermissionError,ConnectionError, and many others.ValueError—correct type, wrong value.TypeError—wrong type entirely.
Raising exceptions
Use raise to signal an error from your own code:
def set_age(value):
if not isinstance(value, int):
raise TypeError(f"Age must be int, got {type(value).__name__}")
if value < 0 or value > 150:
raise ValueError(f"Age {value} is outside the plausible range")
return value
Inside an except block, a bare raise re-raises the current exception without losing the original traceback:
try:
risky_operation()
except SomeError:
log.error("Operation failed", exc_info=True)
raise # re-raises the same exception
Custom exception classes
Defining your own exception types makes error handling more precise and self-documenting:
class AppError(Exception):
"""Base class for all application-level errors."""
class ConfigError(AppError):
"""Raised when configuration is missing or invalid."""
class NetworkError(AppError):
"""Raised when a remote call fails."""
def __init__(self, url, status_code):
self.url = url
self.status_code = status_code
super().__init__(f"HTTP {status_code} from {url}")
Callers can catch the broad AppError or the specific NetworkError as needed.
Practical decision checklist
- Catch exceptions at the level where you can actually do something useful with them.
- Name specific exception types; a bare
exceptcatchesKeyboardInterrupttoo. - Include context in the message: which file, which URL, which value caused the problem.
- Use
finallyfor cleanup, not for control flow. - In library code, define a custom exception hierarchy so callers can choose how finely to handle errors.