Skip to main content
Every program eventually encounters something unexpected: a missing file, an invalid user input, a network timeout, a division by zero. Python’s exception-handling system lets you anticipate these situations, recover gracefully, and give users — and yourself — meaningful error messages instead of confusing stack traces. This page walks you from the basics of try/except all the way to creating your own exception hierarchy.

What is an Exception?

An exception is a runtime error — something went wrong while the program was running, not while it was being compiled. Python stops execution and raises an exception object. If nothing catches it, the program crashes and prints a traceback.

try / except

Wrap risky code in a try block. If an exception occurs, Python jumps to the matching except block instead of crashing:
Without the try/except, a bad input would crash the program. With it, the user gets a friendly message and the program continues.

Catching Specific Exception Types

Always catch the most specific exception type you expect. Catching broad exceptions hides real bugs.

Common Built-in Exception Types

Catching Multiple Types at Once

Accessing the Exception Object

The as error clause binds the exception to a variable, letting you log or display the error message:

The else Clause

The else block runs only if the try block completed without raising any exception. Use it for code that should only execute when the risky operation succeeded:
Put only the minimal risky operation inside try. Move follow-on logic to else. This way you can be certain your except blocks are not silently swallowing unrelated errors.

The finally Clause

The finally block always runs — whether the try succeeded, the except caught something, or even if the program is about to re-raise an exception. Use it to release resources like file handles, database connections, or network sockets:
The with statement (context manager) is usually a cleaner alternative to finally for file handling, because it closes the file automatically even when an exception occurs.

Raising Exceptions

Use raise to trigger an exception yourself when your code detects that a rule has been broken:

Re-raising an Exception

Inside an except block, call raise with no argument to re-raise the current exception after doing some logging or cleanup:

Creating Custom Exception Classes

For larger applications, define domain-specific exceptions by subclassing Exception. This makes error handling precise and self-documenting.
Using the custom exceptions:
Output:

Practical Example: Reading a File with Full Error Handling

The following function reads a configuration file, parses it as JSON, and returns a settings dictionary — with proper handling for every realistic failure mode:

Best Practices

Catch specific exceptions

Never write except: or except Exception: without a good reason. Catching bare exceptions swallows bugs, including KeyboardInterrupt and SystemExit.

Fail fast

Raise an exception as soon as you detect invalid input or an impossible state. Delaying the error makes it much harder to trace back to the root cause.

Log, then decide

Log the original exception before re-raising or raising a new one. Use Python’s logging module — print() is not suitable for production error reporting.

Use custom exceptions

Define a base exception class for your application (e.g., AppError) and derive specific ones from it. Callers can then catch either the specific type or the whole application family.
The else clause is often overlooked but is genuinely useful. It signals clearly that the code in that block depends on the try having succeeded — which is better than tucking that code at the end of the try block where it could accidentally catch unrelated exceptions.