Python Try...Except
Learn Python try, except, else, and finally blocks with clear examples. Handle ZeroDivisionError, ValueError, FileNotFoundError, and more.
When Python encounters an error at runtime, it raises an exception — an object that represents what went wrong. Without any handling, an exception terminates your program immediately. The try...except statement lets you catch these exceptions, respond to them gracefully, and keep your program running.
This chapter covers:
- The
try/exceptblock — catching a specific exception - Catching exception details with
as - Multiple
exceptclauses and catching several exceptions at once - The
elseblock — code that runs only when no exception occurs - The
finallyblock — cleanup code that always runs - Common built-in exceptions and when they occur
- Re-raising exceptions
- Best practices and common mistakes
The basic try...except block
Wrap the code that might fail in a try block. If Python raises an exception, execution jumps to the matching except block instead of crashing.
Output:
Error: division by zeroPython attempts the division, raises ZeroDivisionError, and the except block handles it. The as e clause binds the exception object to e so you can inspect or log the message.
If the try block succeeds, the except block is skipped entirely.
Catching a specific exception type
Always name the exception type you expect. Catching a named type makes your intent clear and avoids accidentally hiding unrelated bugs.
try:
number = int("abc")
except ValueError as e:
print(f"Could not convert: {e}")Output:
Could not convert: invalid literal for int() with base 10: 'abc'int("abc") raises ValueError because "abc" is not a valid integer. Specifying ValueError in the except clause means any other unexpected exception will still propagate and surface as an error rather than being silently swallowed.
Multiple except clauses
A single try block can have several except clauses — Python checks them top to bottom and executes the first match.
def safe_index(items, index):
try:
return items[index]
except IndexError:
print("Index out of range.")
except TypeError:
print("Index must be an integer.")
safe_index([1, 2, 3], 10) # IndexError
safe_index([1, 2, 3], "a") # TypeErrorOutput:
Index out of range.
Index must be an integer.Order matters: place more specific exception types before broader ones so the specific handler runs first.
Catching multiple exceptions in one clause
When two or more exceptions deserve the same response, group them in a tuple:
try:
value = int("not-a-number")
except (ValueError, TypeError) as e:
print(f"Input error: {e}")Output:
Input error: invalid literal for int() with base 10: 'not-a-number'The else block
The else block runs only when the try block completes without raising any exception. Use it for code that should run on success but does not itself need error protection:
try:
result = 10 / 2
except ZeroDivisionError:
print("Cannot divide by zero.")
else:
print(f"Division succeeded. Result: {result}")Output:
Division succeeded. Result: 5.0Keeping success-path logic in else (rather than at the bottom of try) prevents accidentally catching exceptions that the success-path code itself might raise.
The finally block
The finally block executes no matter what — whether the try block succeeds, an exception is caught, or an exception propagates uncaught. Use it for cleanup: closing files, releasing locks, or disconnecting from a database.
try:
result = 10 / 0
except ZeroDivisionError:
print("Error caught.")
finally:
print("This always runs — cleanup goes here.")Output:
Error caught.
This always runs — cleanup goes here.Even if you comment out the except block, finally still executes before Python propagates the exception.
Full structure at a glance
try:
# code that may raise an exception
except SomeException as e:
# handle the exception
except (AnotherError, YetAnother):
# handle either of these
else:
# runs only when try succeeded
finally:
# always runsCommon built-in exceptions
| Exception | When it occurs |
|---|---|
ZeroDivisionError | Division or modulo by zero |
ValueError | Right type, wrong value (e.g. int("abc")) |
TypeError | Operation applied to wrong type |
IndexError | Sequence index out of range |
KeyError | Dictionary key not found |
FileNotFoundError | File or directory does not exist |
AttributeError | Object has no such attribute |
ImportError | Module cannot be imported |
NameError | Variable name is not defined |
All of these inherit from the base class Exception. You can catch Exception to handle any of them in one clause, but prefer specific types where possible.
Handling a missing file
try:
with open("data.txt", "r") as file:
content = file.read()
except FileNotFoundError:
print("Error: File not found.")Output (when data.txt does not exist):
Error: File not found.Re-raising an exception
Sometimes you want to log an exception or do partial cleanup, but still let it propagate to the caller. Use a bare raise inside an except block:
def process():
try:
result = 10 / 0
except ZeroDivisionError:
print("Logging error...")
raise # re-raises the original ZeroDivisionError
try:
process()
except ZeroDivisionError as e:
print(f"Outer handler caught: {e}")Output:
Logging error...
Outer handler caught: division by zeroThe bare raise preserves the original traceback so debugging is not harder than without the handler.
Catching the base Exception class
You can use Exception as a catch-all for any non-system-exiting error:
try:
result = 10 / 0
except Exception as e:
print(f"An error occurred: {type(e).__name__}: {e}")Output:
An error occurred: ZeroDivisionError: division by zerotype(e).__name__ gives you the specific class name even when you catch via the base class. This is useful in top-level handlers where you want to log every unexpected error.
Bare except — and why to avoid it
A bare except (with no exception type) catches literally everything, including KeyboardInterrupt (Ctrl+C) and SystemExit, making your program hard to interrupt:
# Avoid this pattern
try:
result = 10 / 0
except:
print("Some error occurred")Prefer except Exception if you need a broad catch, because it still lets KeyboardInterrupt and SystemExit propagate normally.
Best practices
- Be specific. Catch the narrowest exception type that makes sense.
- Do not silence exceptions. At minimum log the error; never leave an
exceptblock empty. - Use
elsefor success-path code. This keeps thetryblock as small as possible. - Use
finallyfor cleanup. Or, even better, use awithstatement — see Python with Statement. - Avoid bare
except. Useexcept Exceptionif you need a wide net. - Re-raise when appropriate. If you cannot fully handle the error, let it propagate with
raise.
For raising exceptions deliberately and creating your own exception classes, see Python raise and Custom Exceptions.