Learn
🤖 AI Hub 📰 News 📖 Dictionary 🧰 Resources
Grow
💼 Jobs 🚀 Community 🏆 Challenges
Explore by Stage
🌑 Zero 📚 Learn 🔧 Practice 🏗 Build 🚀 Launch 💰 Earn
Account
Login Sign Up
🏗️HTML 🎨CSS ⚡JavaScript 🐙Git & GitHub ⚛️React Soon 📘TypeScript Soon 🐍Python 🗄️Database & SQL Soon 🌐Web Development Soon ☁️Cloud & DevOps Soon 🤖AI & ML Soon 📄Resume Prep Soon 🎤Interview Prep Soon 💼Freelancing Soon 🚀SaaS & Side Projects Soon 🤖Automation Soon
Intermediate 🕑 12 min read 👁 1 views 📅 Oct 22, 2026

Error Handling: try, except, else and finally

Handle Python exceptions with try, except, else and finally, read a traceback, raise your own errors, create a custom exception class and build an ATM input loop that never crashes.

Why This Matters: An ATM That Never Crashes

You walk up to an ATM and type the wrong amount, say Rs 250, when the machine only has Rs 100 and Rs 500 notes. A badly made machine would freeze and keep your card. A good one shows a calm message, “Please enter a multiple of Rs 100”, and lets you try again.

Your Python programs face the same situation all the time. A user types letters instead of digits, a file is missing, a dictionary key does not exist. Without a plan, Python stops the whole program with a long red message.

In this lesson you will learn what exceptions are, how to read a traceback, and how to handle problems with try, except, else and finally. You will also raise your own errors, create a custom exception class and build an input loop that keeps asking until the user enters a valid number.

What Is an Exception?

An exception is Python's way of saying “I cannot continue with this line”. When it happens and nobody handles it, the program stops at once and prints a report. Press Try it yourself: the Input box already contains five hundred.

balance = 5000

amount_text = input("Enter amount: ")
amount = int(amount_text)          # ERROR: ValueError
balance = balance - amount
print("Please take your cash")     # never runs

Python cannot turn the text “five hundred” into a whole number, so int() raises a ValueError, and the last line never runs.

Reading a Traceback

The report Python prints is called a traceback. It looks something like this (newer Python versions also mark the exact spot with ^ characters):

Traceback (most recent call last):
  File "atm.py", line 4, in <module>
    amount = int(amount_text)
ValueError: invalid literal for int() with base 10: 'five hundred'
Read a traceback from the bottom up. The last line tells you what went wrong (the error type and message). The lines just above it tell you where: the file, the line number and the code on that line. In longer programs you will see several File lines, one for each function call that led to the error.

Common Exceptions You Will Meet

ExceptionWhat went wrongExample that causes it
ValueErrorThe type is right but the value is notint("abc")
ZeroDivisionErrorDividing by zero5000 / 0
KeyErrorA dictionary key that does not existaccounts["9999"]
IndexErrorA list position that does not exist[100, 200][5]
FileNotFoundErrorOpening a missing file for readingopen("statement.txt")
TypeErrorA value of the wrong type for the job"Rs " + 500

try and except: Catch the Problem

Put the line that might fail inside a try block. If an exception happens, Python skips the rest of the try block and runs the matching except block instead of stopping. After that, the program continues normally.

amount_text = input("Enter amount: ")

try:
    amount = int(amount_text)
    print(f"Withdrawing Rs {amount}")     # skipped if int() fails
except ValueError:
    print("Please enter the amount in digits, like 500.")

print("Thank you for banking with us.")   # the program keeps going

Change the input to 500 and run it again. Now int() works, the except block is skipped, and both prints run.

Always name the exception you expect, as in except ValueError. You will see in Common Mistakes why a bare except: is dangerous.

Several except Blocks

One piece of code can fail in different ways, and each way deserves its own message. Add one except block per exception type. Python checks them from top to bottom and runs the first one that matches. Writing except ValueError as error also gives you the exception object, so you can print its message.

accounts = {"1001": 5000, "1002": 1200}

def split_balance(account, holders_text):
    """Show each holder's equal share of a joint account."""
    try:
        balance = accounts[account]          # KeyError if the account is unknown
        holders = int(holders_text)          # ValueError if not a whole number
        share = balance / holders            # ZeroDivisionError if holders is 0
        print(f"Account {account}: Rs {share:.2f} each")
    except KeyError:
        print(f"Account {account} was not found.")
    except ValueError as error:
        print("Holders must be a whole number. Details:", error)
    except ZeroDivisionError:
        print("A joint account needs at least 1 holder.")

split_balance("1001", "4")      # Account 1001: Rs 1250.00 each
split_balance("9999", "2")      # Account 9999 was not found.
split_balance("1002", "two")    # Holders must be a whole number. Details: invalid literal ...
split_balance("1002", "0")      # A joint account needs at least 1 holder.

If two exceptions need the same message, list them in brackets: except (KeyError, IndexError):.

else and finally

A try statement can have two more parts:

  • else runs only when the try block finished without an exception. Put the “success” work here.
  • finally runs always, whether there was an exception or not. It is the place for clean-up, like returning the card.
def withdraw(balance, amount_text):
    print("Card inserted")
    try:
        amount = int(amount_text)
    except ValueError:
        print("That is not a valid amount.")
    else:
        # Runs only if the try block had NO error
        balance = balance - amount
        print(f"Take your cash: Rs {amount}. Balance: Rs {balance}")
    finally:
        # Runs ALWAYS, error or not
        print("Please take your card")
    return balance

balance = withdraw(5000, "1500")
print()
balance = withdraw(balance, "1,500")
print()
print("Final balance:", balance)
# Card inserted
# Take your cash: Rs 1500. Balance: Rs 3500
# Please take your card
#
# Card inserted
# That is not a valid amount.
# Please take your card
#
# Final balance: 3500
Keep the try block small. Put only the line that can fail inside try, and the code that should run after success in else. Then an except ValueError can only catch the error you expected, not a surprise from some other line.

raise: Report Your Own Errors

Python raises exceptions for its own problems. Your program has its own rules too, such as “only multiples of Rs 100”. When a rule is broken, use raise with a clear message. The function stops at once, and the caller decides how to show the problem.

def check_amount(amount, balance):
    if amount <= 0:
        raise ValueError("The amount must be more than Rs 0.")
    if amount % 100 != 0:
        raise ValueError("Please enter a multiple of Rs 100.")
    if amount > balance:
        raise ValueError(f"Not enough balance. You have Rs {balance}.")
    print(f"Rs {amount}: OK")

for amount in [2000, 0, 450, 9000]:
    try:
        check_amount(amount, 5000)
    except ValueError as error:
        print(f"Rs {amount}: {error}")
# Rs 2000: OK
# Rs 0: The amount must be more than Rs 0.
# Rs 450: Please enter a multiple of Rs 100.
# Rs 9000: Not enough balance. You have Rs 5000.

Notice the split of jobs: check_amount only checks and raises. The loop outside catches the error and talks to the user. This keeps each part simple.

Custom Exception Classes, Briefly

Sometimes a built-in name like ValueError is too general. You can create your own exception type in two lines with class InsufficientBalanceError(Exception):. The words in brackets mean “this is a kind of Exception”. You will learn classes properly in the Classes and Objects lesson. For now, copy the pattern.

class InsufficientBalanceError(Exception):
    """Raised when the account does not have enough money."""

def withdraw(balance, amount):
    if amount > balance:
        raise InsufficientBalanceError(
            f"You asked for Rs {amount}, but your balance is Rs {balance}."
        )
    return balance - amount

try:
    new_balance = withdraw(3000, 4500)
except InsufficientBalanceError as error:
    print("Sorry!", error)
# Sorry! You asked for Rs 4500, but your balance is Rs 3000.

print(isinstance(InsufficientBalanceError("x"), Exception))   # True

A custom name makes your code read like plain English: “except insufficient balance error”. It also lets you catch only your own problems, while real bugs still show up.

A Safe Input Loop

A real ATM does not give up after one wrong entry. It asks again. Combine while True, try/except and continue to keep asking until the value is valid, then return it. The Input box has three answers ready: abc, then -200, then 500.

def ask_amount():
    while True:
        text = input("Enter amount (multiple of 100): ")
        try:
            amount = int(text)
        except ValueError:
            print("Please type digits only, like 500.")
            continue                    # ask again
        if amount <= 0 or amount % 100 != 0:
            print("Please enter a positive multiple of 100.")
            continue
        return amount                   # a good value: leave the loop

amount = ask_amount()
print("You asked for Rs", amount)

Real-Life Example: The Village Bank ATM

Here is the complete withdrawal screen. ATMError is a parent class for all problems the customer can fix, and the two specific errors are kinds of it. So one except ATMError catches both. read_amount() keeps asking until it gets digits, withdraw() raises an error for a wrong amount or low balance, else pays out, and finally always returns the card. The Input box tries four amounts: five hundred, 250, 9000 and finally 2500.

class ATMError(Exception):
    """Base class for problems the customer can fix."""

class InvalidAmountError(ATMError):
    pass

class InsufficientBalanceError(ATMError):
    pass

def read_amount():
    while True:
        text = input("Enter amount: ").strip()
        try:
            return int(text)
        except ValueError:
            print("  Please type the amount in digits, like 2000.")

def withdraw(balance, amount):
    if amount <= 0 or amount % 100 != 0:
        raise InvalidAmountError("This ATM gives notes of Rs 100 and above. Enter a multiple of 100.")
    if amount > balance:
        raise InsufficientBalanceError(f"Not enough balance. You have Rs {balance:,}.")
    return balance - amount

balance = 6000
print("Welcome to the Village Bank ATM")
try:
    while True:
        amount = read_amount()
        try:
            balance = withdraw(balance, amount)
        except ATMError as error:          # catches both of our error types
            print("  " + str(error))
        else:
            print(f"  Please collect Rs {amount:,}. Balance left: Rs {balance:,}")
            break
finally:
    print("Please take your card. Have a nice day!")

Try these changes:

  1. Change the inputs so the very first amount is valid.
  2. Add a DailyLimitError for amounts above Rs 5,000 and raise it in withdraw().
  3. Count the wrong attempts and stop after three, printing “Too many attempts”.

Common Mistakes

  • Using a bare except:. It catches everything, including your own spelling mistakes. Here the real bug is a wrong variable name, but the user sees a misleading message and you never find out:
amount_text = "500"

try:
    amount = int(amount_txt)          # a spelling mistake in the variable name!
except:
    print("Please enter a valid amount.")   # wrong message: the real bug is hidden

Fix: catch only what you expect. Now the spelling mistake shows up clearly as a NameError, and you can fix it:

amount_text = "500"

try:
    amount = int(amount_txt)          # ERROR: NameError
except ValueError:
    print("Please enter a valid amount.")
  • Putting too much code inside try. A big block can hide unrelated errors behind one message. Fix: wrap only the risky line, and move the rest to else.
  • Putting a general except first. except Exception before except ValueError means the second block can never run. Fix: list the most specific exceptions first.
  • Using exceptions for simple checks. If an if can test it clearly, such as if key in accounts, an if is often easier to read. Use try when the check is hard to do in advance, like converting user text to a number.
  • Hiding the error completely. except ValueError: pass makes problems disappear without a trace. Fix: always print a message, ask again, or log what happened.

Practice Task: The Cash Deposit Machine

Write the logic for a deposit machine with its own limit of Rs 20,000 per deposit. Complete the TODO steps:

  • deposit() raises DepositError when the text is not a number. (Catch the ValueError and raise your own error inside the except.)
  • It also raises DepositError for amounts that are not a multiple of 100, or above the limit.
  • It returns the new balance when everything is fine.
  • The loop uses try, except, else and finally to show each result and “Machine ready” every time.
class DepositError(Exception):
    pass

MAX_DEPOSIT = 20000      # this machine's own limit

def deposit(balance, amount_text):
    # TODO 1: turn amount_text into an int. If that fails, raise
    #         DepositError("Please enter digits only.")
    # TODO 2: raise DepositError if the amount is not a multiple of 100
    # TODO 3: raise DepositError if the amount is more than MAX_DEPOSIT
    # TODO 4: return the new balance
    pass

balance = 1000
for text in ["abc", "150", "25000", "2000"]:
    # TODO 5: call deposit() inside try / except DepositError / else / finally
    #   except: print the error message
    #   else:   print the new balance
    #   finally: print "Machine ready"
    pass
Show one possible answer (try it yourself first!)
class DepositError(Exception):
    pass

MAX_DEPOSIT = 20000      # this machine's own limit

def deposit(balance, amount_text):
    try:
        amount = int(amount_text)
    except ValueError:
        raise DepositError("Please enter digits only.")
    if amount <= 0 or amount % 100 != 0:
        raise DepositError("Please deposit notes: a multiple of Rs 100.")
    if amount > MAX_DEPOSIT:
        raise DepositError(f"You can deposit at most Rs {MAX_DEPOSIT:,} at a time.")
    return balance + amount

balance = 1000
for text in ["abc", "150", "25000", "2000"]:
    print("Deposit:", text)
    try:
        balance = deposit(balance, text)
    except DepositError as error:
        print("  " + str(error))
    else:
        print(f"  Done. New balance: Rs {balance:,}")
    finally:
        print("  Machine ready")
# Deposit: abc
#   Please enter digits only.
#   Machine ready
# Deposit: 150
#   Please deposit notes: a multiple of Rs 100.
#   Machine ready
# Deposit: 25000
#   You can deposit at most Rs 20,000 at a time.
#   Machine ready
# Deposit: 2000
#   Done. New balance: Rs 3,000
#   Machine ready

Key Takeaways

  • An exception stops the program unless you handle it. Read the traceback from the bottom: the error type and message, then the line.
  • try holds the risky line; except SomeError handles one kind of problem; use several except blocks for different messages.
  • else runs only on success; finally runs every time and is perfect for clean-up.
  • Use raise ValueError("clear message") for your own rules, and a custom class like class InsufficientBalanceError(Exception) for named problems.
  • A while True loop with try/except keeps asking until the user enters a valid value. Never use a bare except:.

❓ Frequently Asked Questions

In everyday talk they mean the same thing. In Python, an exception is the object that is raised when something goes wrong while the program runs. A SyntaxError is different: Python finds it before the program even starts, so try cannot catch a mistake in the same file's grammar.
Usually not. Catch the specific exceptions you expect, so that real bugs still appear and you can fix them. except Exception is sometimes used at the very top of a big program to log unexpected problems.
No. Both are optional. Use else when you have success-only work, and finally when something must happen no matter what, such as a clean-up message.
print only shows text and the program carries on as if nothing happened. raise stops the function and lets the code that called it decide what to do, such as asking again or showing the message in a different way.
Was this lesson helpful?

🎯 Finished this lesson?

🔗 Related Tutorials
🤖 Got a Doubt? Ask AI
Get instant answers in Hindi or English
🤖
Z2T Bot
Online
Ask me anything about this tutorial — I'm here! 👋