Start with the study notes on Error Handling (definitions, examples and a quick revision), then work through 30 practice questions, each with the correct answer and a clear explanation of why it is right. When you are ready, take this topic as an interactive quiz to test yourself.
9 easy · 15 medium · 6 hard · Part of Python: Master Python from basics to advanced - 20 topics.
Topic overview
Error Handling – try, except, else and finally
1. Exceptions vs syntax errors
A syntax error breaks Python's grammar (e.g. a missing colon). It is found before the program runs and cannot be caught.
An exception is an error that happens while the program runs. It can be caught and handled.
An uncaught exception stops the program and prints a traceback. Read it from the bottom: the last line names the exception and its message.
2. Common exceptions
ZeroDivisionError:10 / 0.
ValueError: right type, bad value, e.g. int("abc").
TypeError: wrong type, e.g. "hello" + 5.
IndexError: list position out of range.
KeyError: missing dictionary key.
NameError: a name that was never defined.
AttributeError: an attribute or method that doesn't exist.
FileNotFoundError: opening a file that isn't there.
3. try and except
Put the risky code in try. If it raises an exception, the matching except block runs instead of the program crashing. Use as e to get the message.
try:
n = int("abc")
except ValueError as e:
print("Bad value:", e)
print("program continues")
OutputBad value: invalid literal for int() with base 10: 'abc'
program continues
Use several except blocks for different handling. Python runs the first one that matches.
Use a tuple when several exceptions share one handler: except (ValueError, TypeError):.
for text in ["5", "zero", "0"]:
try:
print(10 / int(text))
except ValueError:
print("not a number")
except ZeroDivisionError:
print("can't divide by zero")
Output2.0
not a number
can't divide by zero
5. else and finally
else runs only if the try block finished without an exception.
finally runs always, even after a return. Use it for cleanup.
B An error that occurs WHILE the program runs, which can be caught and handled
C A warning message
D A special comment
Answer: B — An error that occurs WHILE the program runs, which can be caught and handled
Exceptions are runtime errors - division by zero, missing file, bad conversion. Unlike syntax errors (caught before the program starts), exceptions happen mid-run and CAN be caught with try/except.
Runtime errorsSyntaxError vs exceptions
Q2.easy
Which is the correct basic structure for handling an error?
try:
n = int(input("Number: "))
except ValueError:
print("That's not a number")
A try:\n risky()\nexcept:\n handle()
B attempt:\n risky()\nrescue:\n handle()
C try risky() catch handle()
D if error: handle()
Answer: A — try:\n risky()\nexcept:\n handle()
Python uses try/except: the risky code goes in try; if it raises an exception, the except block runs instead of crashing.
try/except
Q3.easy
What exception does 10 / 0 raise?
try:
print(10 / 0)
except ZeroDivisionError:
print("Can't divide by zero!")
A ValueError
B ZeroDivisionError
C MathError
D TypeError
Answer: B — ZeroDivisionError
Dividing by zero raises ZeroDivisionError. Its message: 'division by zero'.
ZeroDivisionError
Q4.easy
What exception does int("abc") raise?
try:
n = int("abc")
except ValueError as e:
print("Bad value:", e)
A TypeError
B NameError
C ValueError
D ConversionError
Answer: C — ValueError
The TYPE is right (a string) but the VALUE can't be converted - that's ValueError. TypeError would be for a wrong type entirely, like int([1,2]).
ValueErrorTypeError
Q6.easy
Using a variable that was never defined raises:
A NameError
B ValueError
C KeyError
D UndefinedError
Answer: A — NameError
NameError: name 'x' is not defined - Python doesn't know that name. Often caused by typos in variable names.
NameErrorTypos
Q7.easy
What happens to a program when an exception is NOT caught?
A It continues from the next line
B It stops and prints a traceback showing where the error happened
C It restarts
D Nothing visible
Answer: B — It stops and prints a traceback showing where the error happened
An unhandled exception crashes the program and prints a traceback - the chain of calls that led to the error, ending with the exception type and message. Learning to READ tracebacks is a superpower.
Read tracebacks bottom-up: the last line names the error; lines above show where.
TracebackDebugging
Q14.easy
Which statement deliberately TRIGGERS an exception?
def set_age(age):
if age < 0:
raise ValueError("age cannot be negative")
return age
A raise
B throw
C error
D panic
Answer: A — raise
raise fires an exception on purpose: raise ValueError("age cannot be negative"). (throw is Java/JavaScript.)
raiseValidation
Q26.easy
"hello" + 5 raises which exception?
A ValueError
B TypeError
C NameError
D SyntaxError
Answer: B — TypeError
Mixing incompatible TYPES (str + int) is TypeError: can only concatenate str (not "int") to str. Compare: int("abc") is ValueError - right type, bad value.
TypeErrorValueError
Q29.easy
Which of these is a SYNTAX error rather than an exception?
A if x > 5 (missing colon)
B 10 / 0
C int("abc")
D nums[99]
Answer: A — if x > 5 (missing colon)
A missing colon breaks Python's grammar - detected BEFORE the program even starts, and try/except cannot catch it. The other three are runtime exceptions.
SyntaxErrorRuntime vs parse time
Medium questions (15)
Q5.medium
Match the exception: accessing nums[10] when nums has 3 items?
nums = [1, 2, 3]
# nums[10] # IndexError: list index out of range
d = {"a": 1}
# d["z"] # KeyError: 'z'
A KeyError
B IndexError
C ValueError
D RangeError
Answer: B — IndexError
Lists raise IndexError for out-of-range positions. KeyError is the dict equivalent (missing key). There is no RangeError in Python.
IndexErrorKeyError
Q8.medium
In a try/except, what runs when NO exception occurs?
try:
print("works fine")
except ValueError:
print("never printed here")
# output: works fine
A Both try and except blocks
B Only the try block - except is skipped entirely
C Only the except block
D Neither
Answer: B — Only the try block - except is skipped entirely
except is the emergency exit - it only runs when its exception actually happens. On a clean run, the try body completes and except is ignored.
Control flow
Q9.medium
How do you get the error MESSAGE inside except?
try:
int("abc")
except ValueError as e:
print("Problem:", e)
# Problem: invalid literal for int() with base 10: 'abc'
A except ValueError as e: print(e)
B except ValueError: print(error)
C except ValueError(e): print(e)
D print(except.message)
Answer: A — except ValueError as e: print(e)
'as e' binds the exception object to a name; printing it shows the message. e is only available inside that except block.
Exception objectsas keyword
Q10.medium
How do you handle TWO different exceptions DIFFERENTLY?
try:
n = int(input())
print(10 / n)
except ValueError:
print("Not a number")
except ZeroDivisionError:
print("Can't divide by zero")
A Multiple except blocks, one per exception type
B Two try blocks
C except handles everything the same way always
D Use if/else inside try
Answer: A — Multiple except blocks, one per exception type
Stack except blocks - Python runs the FIRST one matching the raised exception. Order specific-to-general.
Multiple exceptException matching
Q11.medium
How do you catch ValueError AND TypeError with the SAME handler?
try:
risky()
except (ValueError, TypeError) as e:
print("Bad input:", e)
A except (ValueError, TypeError):
B except ValueError and TypeError:
C except ValueError, TypeError:
D except [ValueError, TypeError]:
Answer: A — except (ValueError, TypeError):
A TUPLE of exception types shares one handler: except (ValueError, TypeError) as e. Option C was Python 2 syntax and is now an error.
Answer: C — ALWAYS - success, exception, even a return
finally is guaranteed cleanup - it runs no matter how the try block exits: normally, via an exception (caught or not), or via return/break. Perfect for closing files and connections.
finallyCleanup
Q15.medium
Why raise an exception in your OWN function instead of returning -1 or None on bad input?
A Exceptions can't be ignored silently, carry a clear message, and stop bad data spreading
B Exceptions are faster
C return doesn't work for errors
D No reason - error codes are better
Answer: A — Exceptions can't be ignored silently, carry a clear message, and stop bad data spreading
A returned -1 can be forgotten and treated as data; a raised exception demands attention and pinpoints the problem. 'Errors should never pass silently' - the Zen of Python.
raiseAPI design
Q17.medium
Trying to open a file that doesn't exist raises:
try:
f = open("nofile.txt")
except FileNotFoundError:
print("File not found - creating a new one")
A FileNotFoundError
B IOError only in Python 2
C MissingFileError
D KeyError
Answer: A — FileNotFoundError
open("nofile.txt") raises FileNotFoundError - one of the most commonly handled exceptions in real programs.
FileNotFoundErrorFiles
Q18.medium
What is the output?
try:
print("A")
x = 1 / 0
print("B")
except ZeroDivisionError:
print("C")
print("D")
A A C D
B A B C D
C A C
D C D
Answer: A — A C D
'A' prints, then 1/0 raises - the rest of try ('B') is SKIPPED, control jumps to except ('C'), and the program continues normally after ('D').
Key point: the try block stops at the line that raised.
Control flowTracing
Q19.medium
Which loop keeps asking until the user types a valid number?
while True:
try:
n = int(input("Number: "))
break
except ValueError:
print("Not a number - try again")
A while True:\n try:\n n = int(input()); break\n except ValueError:\n print("try again")
B try until valid: n = int(input())
C n = int(input()) repeat except
D for i in valid: input()
Answer: A — while True:\n try:\n n = int(input()); break\n except ValueError:\n print("try again")
The standard robust-input pattern: loop forever, try the conversion, break on success, catch ValueError and re-ask on failure.
Input validationwhile True
Q23.medium
Inside except, what does a bare 'raise' (no arguments) do?
try:
process()
except ValueError:
print("logging the error...")
raise # pass it up unchanged
A Re-raises the SAME exception it just caught
B Raises a new generic Exception
C Cancels the exception
D SyntaxError
Answer: A — Re-raises the SAME exception it just caught
Bare raise re-throws the current exception - useful to log or clean up but still let the error propagate upward for someone else to handle.
Re-raisingLogging
Q24.medium
except Exception: catches...
A Almost all normal errors - but NOT KeyboardInterrupt or SystemExit
B Absolutely everything including Ctrl+C
C Only built-in exceptions
D Only one error per program
Answer: A — Almost all normal errors - but NOT KeyboardInterrupt or SystemExit
Nearly every error inherits from Exception. KeyboardInterrupt (Ctrl+C) and SystemExit inherit from BaseException instead - deliberately, so except Exception doesn't block a user trying to quit. That's why it's safer than a bare except.
Exception hierarchyBaseException
Q25.medium
What is the output?
def f():
try:
return "from try"
finally:
print("cleanup")
print(f())
finally runs even on return - the return value is computed, finally executes ('cleanup'), THEN the function actually returns ('from try' is printed by the outer print).
finallyreturn interaction
Q27.medium
Where in a traceback do you find the actual error?
A The FIRST line
B The LAST line names the exception; the lines above show the path of calls to it
C The middle
D Tracebacks don't show errors
Answer: B — The LAST line names the exception; the lines above show the path of calls to it
Read from the bottom: last line = exception type + message; the 'File ..., line ...' entries above show where - the DEEPEST one is usually your bug. 'most recent call last' means the bottom entries are closest to the error.
TracebackDebugging
Q30.medium
Best practice: which code goes INSIDE the try block?
try:
n = int(text) # only the risky line
except ValueError:
n = 0
result = n * 2 # safe code outside
A As little as possible - only the line(s) that can actually fail
B The whole program
C Only print statements
D Nothing - try can be empty
Answer: A — As little as possible - only the line(s) that can actually fail
Small try blocks make it obvious WHICH operation failed and avoid accidentally catching unrelated bugs. Wrap the risky line, handle the specific error, keep the rest outside (or in else).
Best practicestry/else
Hard questions (6)
Q12.hard
What does the ELSE clause do in try/except/else?
try:
n = int(text)
except ValueError:
print("bad input")
else:
print("got number", n) # only on success
A Runs when an exception WAS raised
B Runs only when the try block completed WITHOUT any exception
C Always runs
D Same as finally
Answer: B — Runs only when the try block completed WITHOUT any exception
else is the 'success path' - it runs after a clean try. Putting follow-up code there (rather than inside try) keeps the try block small, so you only catch errors from the truly risky line.
try/elseMinimal try blocks
Q16.hard
Why is a BARE except (catching everything) considered bad practice?
# BAD - hides all bugs:
try: process()
except: pass
# GOOD:
try: process()
except FileNotFoundError:
print("file missing")
A It's slower
B It hides real bugs and even catches Ctrl+C - you can't tell WHAT went wrong
C It's a syntax error in Python 3
D It only catches ValueError
Answer: B — It hides real bugs and even catches Ctrl+C - you can't tell WHAT went wrong
except: swallows EVERYTHING - typos (NameError), logic bugs, even KeyboardInterrupt - making failures invisible. Catch the specific exceptions you expect; at worst use except Exception as e and log it.
A Raises AssertionError with that message if the condition is False
B Prints the message always
C Converts x to positive
D Nothing in any mode
Answer: A — Raises AssertionError with that message if the condition is False
assert is a development-time sanity check: if the condition fails, AssertionError is raised with your message. Note: assertions are STRIPPED when Python runs with -O, so never use them for real input validation.
assertAssertionError
Q21.hard
What is EAFP - the Pythonic error philosophy?
# EAFP (Pythonic):
try:
value = d["key"]
except KeyError:
value = default
# LBYL:
if "key" in d:
value = d["key"]
A 'Easier to Ask Forgiveness than Permission' - just try it and catch the exception, instead of checking first
B 'Every Assertion Fails Predictably'
C A testing framework
D 'Except All, Fix Later'
Answer: A — 'Easier to Ask Forgiveness than Permission' - just try it and catch the exception, instead of checking first
EAFP: try the operation and handle failure - vs LBYL ('Look Before You Leap'): check conditions first. Python favors EAFP: it avoids race conditions and duplicate checks.
EAFP vs LBYLPython philosophy
Q22.hard
How do you define your OWN exception type?
class InsufficientFunds(Exception):
pass
def withdraw(bal, amt):
if amt > bal:
raise InsufficientFunds(f"short by {amt - bal}")
A class InsufficientFunds(Exception): pass
B def InsufficientFunds(Exception):
C exception InsufficientFunds:
D new Exception("InsufficientFunds")
Answer: A — class InsufficientFunds(Exception): pass
Custom exceptions are classes inheriting from Exception - often just 'pass' bodies. They let callers catch YOUR specific error: except InsufficientFunds.
Custom exceptionsInheritance
Q28.hard
What is the output?
try:
try:
1 / 0
except ValueError:
print("inner")
except ZeroDivisionError:
print("outer")
A inner
B outer
C inner outer
D Uncaught error
Answer: B — outer
The inner except only handles ValueError - it does NOT match ZeroDivisionError, so the exception keeps travelling outward until the OUTER handler matches: 'outer'. Unmatched exceptions bubble up.