Exception Handling

Ka Kavitha V Updated 03 Oct 2026
7 min read

Exception Handling in Python

Even correct programs encounter problems while running: a user types letters where a number is expected, a file is missing, a network request fails, or a calculation divides by zero. When such an error occurs, Python raises an exception — and if nothing handles it, the program crashes and stops.

Exception handling lets you catch these runtime errors and respond gracefully — showing a helpful message, using a default value, or retrying — instead of letting the program crash.

Errors vs Exceptions

Python has two broad kinds of errors:

KindWhen It HappensExampleCan Be Handled?
Syntax errorBefore the program runs, when Python reads the codeA missing colon or bracketNo — fix the code
ExceptionWhile the program is runningDividing by zero, a missing fileYes — with try and except

Common Built-in Exceptions

ExceptionRaised When
NameErrorA variable or name is not defined
TypeErrorAn operation is used with the wrong type, such as "5" + 5
ValueErrorA value has the right type but is invalid, such as int("abc")
ZeroDivisionErrorA number is divided by zero
IndexErrorA list index is out of range
KeyErrorA dictionary key does not exist
FileNotFoundErrorA file cannot be found

The Exception Handling Blocks

Exception handling uses four blocks:

BlockPurpose
tryContains code that might raise an error
exceptRuns if an error occurs in the try block
elseRuns only if no error occurs
finallyRuns always, whether an error occurred or not

Syntax

try:
    # code that might fail
except SomeError:
    # code that handles the error
else:
    # code that runs if no error occurred
finally:
    # code that always runs

Only try and at least one except (or a finally) are required. else and finally are optional.

Basic Exception Handling

A Program Without Exception Handling

print(total)
print("This line never runs")

Output:

Traceback (most recent call last):
  File "main.py", line 1, in <module>
    print(total)
NameError: name 'total' is not defined

The program stops at the first line. The traceback shows where the error happened and what type it was.

Handling the Error With try and except

try:
    print(total)
except NameError:
    print("An error occurred: variable is not defined")

print("The program continues")

Expected output:

An error occurred: variable is not defined
The program continues

How it works:

  1. Python runs the try block.
  2. print(total) raises a NameError.
  3. Python immediately jumps to the matching except block, skipping any remaining lines in try.
  4. After the except block finishes, the program continues normally.

Bare except vs Specific Exceptions

You may see a bare except: that catches every exception:

try:
    print(total)
except:
    print("Something went wrong")

This works, but it is risky: it also hides unexpected bugs and even catches attempts to stop the program with Ctrl + C. Always catch the specific exception you expect. If you need a general fallback, use except Exception: instead of a bare except:.

Handling Multiple Exceptions

Use several except blocks to handle different errors in different ways. Python checks them from top to bottom and runs the first one that matches.

try:
    number = int("abc")
except ValueError:
    print("Conversion failed: not a valid number")
except Exception:
    print("An unexpected error occurred")

Expected output:

Conversion failed: not a valid number

int("abc") raises a ValueError, which matches the first block. The general except Exception: block acts as a safety net for any other error.

Order matters: Put specific exceptions before general ones. If except Exception: came first, it would catch the ValueError, and the specific message would never be shown.

Handling Several Exceptions in One Block

try:
    result = 10 / int("0")
except (ValueError, ZeroDivisionError):
    print("Please enter a non-zero number")

Expected output:

Please enter a non-zero number

Accessing the Error Message

Use as to give the exception a name and read its message:

try:
    number = int("abc")
except ValueError as error:
    print("Error details:", error)

Expected output:

Error details: invalid literal for int() with base 10: 'abc'

The else Block

The else block runs only if the try block finished without any exception.

try:
    result = 10 / 2
except ZeroDivisionError:
    print("Cannot divide by zero")
else:
    print("Division successful:", result)

Expected output:

Division successful: 5.0

Why not put the print() inside try? Keeping the try block small — containing only the code that might fail — makes it clear which error you are handling, and prevents accidentally catching errors from unrelated code.

The finally Block

The finally block always runs — whether an exception occurred, was handled, or not. It is used for cleanup tasks such as closing files, closing database connections, or releasing other resources.

try:
    value = int("20")
except ValueError:
    print("Invalid input")
finally:
    print("Execution completed")

Expected output:

Execution completed

With invalid input, the finally block still runs:

try:
    value = int("twenty")
except ValueError:
    print("Invalid input")
finally:
    print("Execution completed")

Expected output:

Invalid input
Execution completed

All Four Blocks Together

def divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("Error: division by zero")
    else:
        print("Result:", result)
    finally:
        print("Done\n")

divide(10, 4)
divide(10, 0)

Expected output:

Result: 2.5
Done

Error: division by zero
Done

Real-World Example: File Handling With Exceptions

The finally block ensures a file is closed, even if reading it fails. The file must be opened before the try block, so that finally only runs close() on a file that was actually opened:

try:
    file = open("sample.txt", "r")
except FileNotFoundError:
    print("File not found")
else:
    try:
        content = file.read()
        print(content)
    finally:
        file.close()

If sample.txt does not exist, the output is:

File not found

Why not close the file in a single finally? A common version of this example puts file = open(...) inside try and file.close() in finally. If the file does not exist, open() fails, the variable file is never created, and file.close() in finally raises a new NameError.

In modern Python, the with statement handles closing automatically and is the recommended approach:

try:
    with open("sample.txt", "r") as file:
        print(file.read())
except FileNotFoundError:
    print("File not found")

The with statement closes the file as soon as its block ends, even if an error occurs. You will learn more in the File Handling lessons.

Raising Exceptions Manually

Your own code can raise exceptions with the raise keyword. This is how a function reports that it was given invalid data or cannot complete its task.

Raising a General Exception

score = -5

if score < 0:
    raise Exception("Score cannot be negative")

Output:

Exception: Score cannot be negative

Raising a Specific Exception Type

It is better to raise the built-in exception type that best describes the problem, such as ValueError or TypeError.

age = "twenty"

if not isinstance(age, int):
    raise TypeError("Age must be an integer")

Output:

TypeError: Age must be an integer
  • Use TypeError when a value is the wrong type.
  • Use ValueError when a value is the right type but unacceptable (such as a negative age).

Practical Example: Validating and Handling Together

def set_age(age):
    if age < 0:
        raise ValueError("Age cannot be negative")
    return age

try:
    set_age(-3)
except ValueError as error:
    print("Invalid age:", error)

Expected output:

Invalid age: Age cannot be negative

The function raises the error when the rule is broken, and the calling code handles it. Separating the two keeps functions focused and lets each caller decide how to respond.

Custom Exceptions

For application-specific errors, define your own exception class by inheriting from Exception:

class InsufficientBalanceError(Exception):
    pass

balance = 500
withdrawal = 800

try:
    if withdrawal > balance:
        raise InsufficientBalanceError("Not enough balance for this withdrawal")
except InsufficientBalanceError as error:
    print(error)

Expected output:

Not enough balance for this withdrawal

Custom exceptions make error handling more precise and self-documenting. (Classes and inheritance are covered in the object-oriented programming lessons.)

Why Use Exception Handling?

  • Prevents crashes — the program can recover from expected problems.
  • Improves user experience — users see clear messages instead of tracebacks.
  • Makes debugging easier — specific exceptions and messages pinpoint problems.
  • Ensures clean resource management — files and connections are always closed.

Common Mistakes

MistakeProblem
Using a bare except:Hides unexpected bugs; catch specific exceptions
except Exception: before specific handlersSpecific handlers never run
Putting too much code in tryUnclear which line caused the error
Silently ignoring errors (except: pass)Problems go unnoticed
Using variables in finally that may not existRaises a new NameError
Using exceptions instead of simple checksUse if for normal conditions; exceptions are for exceptional situations
  • Python Type Casting — handling ValueError from int() and float()
  • Python File Handling — handling FileNotFoundError and using with
  • Python Classes and Inheritance — creating custom exception classes

0 Comments

Reviewed before they appear

No comments yet.

Python
Ask about this post
AI Ask about this post

Ask questions about Exception Handling and get answers drawn from it.

Signed-in readers only.