Python programs fail at runtime when operations like file access, parsing input, or network calls raise exceptions. Handling these with try/except keeps your app responsive, preserves useful context, and guarantees cleanup.
Before you start — core rules
- Catch the most specific exception types you expect (for example,
ValueError,FileNotFoundError), not a blanket catch-all. - Use
elsefor the success path andfinallyfor code that must run no matter what. - Prefer context managers (
with) for files and other resources to simplify cleanup. - Let unexpected bugs propagate; log them and re-raise instead of silently swallowing them.
- Reserve catch-all handlers (
except Exceptionorexcept BaseException) for top-level boundaries where you can fail fast or report clearly.
Method 1 — Catch specific exceptions (recommended)
raw = input("Select a fruit number (0-2): ")
try:
selection = ["apple", "pear", "banana"][int(raw)]
print("You chose:", selection)
except ValueError:
print("Not a number.")
except IndexError:
print("Choice out of range.")
try:
selection = ["apple", "pear", "banana"][int(raw)]
except (ValueError, IndexError) as e:
print("Invalid selection:", type(e).__name__)
Common specific exceptions include OSError family (files), ValueError and TypeError (data), ZeroDivisionError (math), and KeyError/IndexError (collections).
Join readers who trust AllThings.How
Add us as a preferred source on Google so our practical guides show up first next time you search.
Add to Google Preferences →Method 2 — Use else and finally to separate success and cleanup
try:
f = open("config.json", "r", encoding="utf-8")
except FileNotFoundError:
print("No config file found.")
else:
data = f.read()
print("Loaded", len(data), "bytes.")
f.close()
f = None
try:
f = open("config.json", "r", encoding="utf-8")
data = f.read()
except OSError as err:
print("OS error:", err)
finally:
if f:
f.close()
Method 3 — Guard your entry point to fail fast and log
def main():
# core workflow
# ...
return 0
import logging, sys
logging.basicConfig(level=logging.INFO)
if __name__ == "__main__":
try:
code = main()
except Exception:
logging.exception("Unhandled error")
sys.exit(1)
sys.exit(code)
# in main(), return 1 or raise an exception when a critical step fails
Method 4 — Manage resources with with (preferred over manual finally)
from pathlib import Path
try:
with Path("data.txt").open("r", encoding="utf-8") as f:
print(f.readline().strip())
except FileNotFoundError:
print("Create data.txt first.")
try:
with open("image.png", "rb") as fh:
blob = fh.read()
except OSError as e:
print("File access failed:", e)
# decode/process blob in a separate block
Method 5 — Raise, re-raise, and chain exceptions for clear error paths
def parse_age(s: str) -> int:
if not s.isdigit():
raise ValueError("age must contain only digits")
age = int(s)
if age < 0:
raise ValueError("age cannot be negative")
return age
import logging
try:
do_risky_thing()
except OSError as err:
logging.error("OS error: %s", err)
raise
from pathlib import Path
import json
def load_json(path: str):
try:
text = Path(path).read_text(encoding="utf-8")
except OSError as e:
raise RuntimeError(f"Failed to read {path}") from e
return json.loads(text)
Method 6 — Catch-all handling at boundaries (use sparingly)
except Exception as e to capture any non–system-exiting error, log details, and decide whether to stop or retry.import traceback
try:
risky()
except Exception as e:
print(f"Unexpected {type(e).__name__}: {e}")
traceback.print_exc() # full stack trace
# decide: raise, return error, or abort
BaseException if you intentionally intercept KeyboardInterrupt or SystemExit, and then re-raise immediately.try:
service_loop()
except BaseException as e:
print(f"Caught {type(e).__name__}; shutting down cleanly.")
raise # never swallow interrupts or SystemExit
Method 7 — Handle multiple failures with ExceptionGroup and except* (Python 3.11+)
def run_tests(tests):
errors = []
for t in tests:
try:
t.run()
except Exception as e:
e.add_note(f"Test {t.name} failed")
errors.append(e)
if errors:
raise ExceptionGroup("Batch failures", errors)
try:
run_tests(tests)
except* (ValueError, TypeError):
print("Some data errors occurred.")
except* OSError:
print("Some OS errors occurred.")
Quick troubleshooting patterns
- Input parsing: wrap
int()orfloat()withexcept ValueErrorand reprompt the user. - File I/O: catch
FileNotFoundError,PermissionError, or genericOSError, and switch to a fallback path. - Networking/API calls: catch library-specific timeouts/connection errors and implement a bounded retry with backoff.
- Always log the exception type and message; for full traces, use
logging.exception()ortraceback.print_exc().
A few focused patterns like specific except clauses, else/finally, and top-level guards will make your Python programs more resilient without hiding real bugs.
