When writing Python programs, errors are inevitable. Whether you’re reading a file, parsing user input, or making network requests, things can (and will) go wrong at runtime. If not handled properly, these failures can crash your application and frustrate users.

That’s where exception handling in Python comes in. By mastering try/except, else, finally, and context managers (with), you can write resilient code that handles errors gracefully while still surfacing real bugs when they occur.

In this guide, we’ll walk through the best practices for handling exceptions in Python, complete with code examples, so you can keep your programs reliable, readable, and production-ready.

Core Principles of Exception Handling in Python

Before diving into methods, keep these golden rules in mind:

  • Catch only specific exceptions you expect (ValueError, FileNotFoundError) instead of using blanket handlers.
  • Use else for success paths and finally for guaranteed cleanup.
  • Favor context managers (with) for files and resources.
  • Never silently swallow bugs — log and re-raise them instead.
  • Reserve catch-all handlers for top-level boundaries where you need to log or fail fast.
READ 👉  KDE Neon: The Best Linux Distro for KDE Enthusiasts

Method 1 — Catch Specific Exceptions (Recommended)

Instead of handling every possible error, catch the exact ones you expect:

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.")

You can also group exceptions when recovery is the same:

try:
    selection = ["apple", "pear", "banana"][int(raw)]
except (ValueError, IndexError) as e:
    print("Invalid selection:", type(e).__name__)

Common exceptions to know:

  • OSError family → File handling
  • ValueError / TypeError → Data issues
  • ZeroDivisionError → Math errors
  • KeyError / IndexError → Collections

Method 2 — Use else and finally for Clean Separation

else runs only if no error occurs, while finally always runs for 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()

With finally:

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 the Program Entry Point

Wrap your main workflow with a top-level exception handler to log and fail fast.

import logging, sys

logging.basicConfig(level=logging.INFO)

def main():
    # core workflow
    return 0

if __name__ == "__main__":
    try:
        code = main()
    except Exception:
        logging.exception("Unhandled error")
        sys.exit(1)
    sys.exit(code)

Returning a non-zero exit code signals automation tools that the run failed.

Method 4 — Use Context Managers (with) for Resources

Context managers automatically clean up files, sockets, and other resources.

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.")

Keep only risky operations inside the try block:

try:
    with open("image.png", "rb") as fh:
        blob = fh.read()
except OSError as e:
    print("File access failed:", e)

# process blob outside the block

Method 5 — Raise, Re-Raise, and Chain Exceptions

Raise when conditions fail:

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

Re-raise after logging:

import logging

try:
    do_risky_thing()
except OSError as err:
    logging.error("OS error: %s", err)
    raise

Chain exceptions with context:

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 (Use Sparingly)

Sometimes you need a broad safety net:

import traceback

try:
    risky()
except Exception as e:
    print(f"Unexpected {type(e).__name__}: {e}")
    traceback.print_exc()

Avoid catching BaseException unless you’re handling shutdown signals:

try:
    service_loop()
except BaseException as e:
    print(f"Caught {type(e).__name__}; shutting down cleanly.")
    raise

Method 7 — Handle Multiple Errors with ExceptionGroup (Python 3.11+)

Aggregate multiple failures with ExceptionGroup:

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)

Handle them selectively with except*:

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() or float() with except ValueError
  • File I/O → catch FileNotFoundError, PermissionError, or OSError
  • Networking/API calls → handle timeouts, retries, and connection errors
  • Always log exceptions → use logging.exception() or traceback.print_exc()

Conclusion

Exception handling in Python isn’t just about preventing crashes — it’s about writing clean, maintainable, and fault-tolerant code. By catching specific errors, using else and finally wisely, leveraging context managers, and applying catch-all strategies only when appropriate, you can create applications that are both robust and easy to debug.

READ 👉  How to Fix “Can’t Find the Specified File” Error When Renaming Files in Windows 11

Master these techniques, and you’ll be equipped to handle anything Python throws your way — from simple input mistakes to complex multi-error workflows.

Did you enjoy this article? Feel free to share it on social media and subscribe to our newsletter so you never miss a post!

And if you'd like to go a step further in supporting us, you can treat us to a virtual coffee ☕️. Thank you for your support ❤️!
Buy Me a Coffee

Categorized in: