Python Multiple Exceptions
Table of Contents + −
In the last lesson you learned try and except. There you caught an error and stopped your program from crashing. But you caught everything the same way. Here is the thing. Not every error means the same problem. A wrong-looking number and a divide-by-zero are two different mistakes. So if you show the same message for both, the person reading it has no idea what really went wrong. This lesson fixes that. You will learn to handle each kind of error in its own way.
🤔 Why Handle Errors Separately?
Picture a small calculator. The person types a number. Then you divide 100 by it. Two different things can go wrong here.
- They type “hello” instead of a number. Converting it fails with a
ValueError. - They type
0. Dividing by it fails with aZeroDivisionError.
These are not the same problem. One is bad input. The other is valid input that math does not allow. If you catch both with one message like “Something went wrong”, you are hiding which one happened. The fix is simple. You write a separate except block for each exception type. Then each error gets its own clear reply.
🧩 Several except Blocks, One try
You can stack more than one except under a single try. Python checks them top to bottom. It runs the first one that matches the error.
Here is the calculator handling both errors on their own terms.
try: number = int(input("Enter a number: ")) result = 100 / number print(f"100 divided by {number} is {result}")except ValueError: print("That was not a whole number. Please type digits only.")except ZeroDivisionError: print("You cannot divide by zero. Try a different number.")Let us walk through it line by line.
int(input(...))reads what the person typed and tries to turn it into a whole number. If they type letters, this raisesValueError.100 / numberdoes the division. Ifnumberis0, this raisesZeroDivisionError.- The first
except ValueError:runs only when the conversion failed. - The second
except ZeroDivisionError:runs only when the division failed.
Now each problem gets its own answer. If someone types “hello”, they see this.
Output
Enter a number: helloThat was not a whole number. Please type digits only.And if someone types 0, they see a completely different message.
Output
Enter a number: 0You cannot divide by zero. Try a different number.Same try, but two clear and separate replies. That is the whole point.
Note
Python runs only one except block per error. The moment a block matches, the rest are skipped. So put the more specific errors near the top.
🧩 Catching Several Types in One Block
Sometimes two different errors should get the same reply. Writing a separate block for each would just repeat yourself. Instead, you can list the types together in a tuple inside one except.
A tuple here is just a group of exception types written inside round brackets and separated by commas.
This example treats both a wrong type and a wrong value as “bad input”. It answers them the same way.
def to_amount(text): try: return float(text) except (TypeError, ValueError): print(f"Cannot read '{text}' as an amount. Using 0 instead.") return 0.0
print(to_amount("19.99"))print(to_amount("price"))print(to_amount(None))Reading it from the top.
float(text)tries to turn the input into a decimal number."price"is a string that is not a number, sofloat()raises aValueError.Noneis not text at all, sofloat()raises aTypeError.- The one
except (TypeError, ValueError):catches both kinds and gives the same friendly fallback.
Running it prints this.
Output
19.99Cannot read 'price' as an amount. Using 0 instead.0.0Cannot read 'None' as an amount. Using 0 instead.0.0So the tuple is for when errors are different in name but the same in meaning to you. Group them. Keep separate blocks when the replies need to be different.
Here is a quick way to decide which approach fits.
| Situation | What to use |
|---|---|
| Each error needs its own message or action | Separate except blocks |
| Different errors, but you react the same way | One except with a tuple of types |
⚠️ Why a Bare except Is Risky
You will see code that just says except: with no error type. That catches everything. It sounds safe, but it actually hides bugs from you.
The problem is that a bare except also swallows mistakes you would want to know about. A typo in a variable name, a wrong function call, even the person pressing Ctrl+C to stop the program. All of it gets caught and quietly ignored. So your program looks like it is working when it is really broken.
Look at the difference.
# ❌ Avoid: catches everything, even real bugs and typostry: result = 100 / numberexcept: print("Something went wrong")
# ✅ Good: name the errors you actually expecttry: result = 100 / numberexcept ZeroDivisionError: print("You cannot divide by zero.")except NameError: print("That variable was never set.")The good version tells you what went wrong. The bad version hides it. When you name the exception, you are saying “I expected this one and I know how to deal with it.” Anything you did not expect is still allowed to surface. That is exactly what you want while building.
Caution
If you really must catch a wide range of errors, write except Exception: instead of a bare except:. It still lets system signals like Ctrl+C pass through. So you do not accidentally trap the person trying to quit.
⚠️ Common Mistakes
A few things trip people up when they start stacking except blocks. The most common one is putting a broad type before a specific one. If except Exception: sits above except ValueError:, the broad block grabs the value error first. So the specific block never runs. Always put specific errors on top.
Another one is reaching for a bare except: and then wondering why real bugs disappear. Name your exceptions instead. People also write several except lines when one tuple would do the job, just because the reply is identical. That is extra code for no reason. And finally, remember that only one block runs per error. You cannot expect two blocks to both fire for the same single failure.
✅ Best Practices
The simplest rule is to catch the most specific exception you can. It makes your intent clear and keeps real bugs visible. When two errors truly deserve the same response, group them with a tuple like except (A, B):. Order your blocks from specific at the top to general at the bottom. If you need a catch-all, prefer except Exception: over a bare except:. And keep each message plain and useful. Tell the person what to do next, not just that “an error” happened.
🧩 What You’ve Learned
- ✅ You can stack several
exceptblocks under onetry, and Python runs the first one that matches. - ✅ Different error types like
ValueErrorandZeroDivisionErrorcan each get their own reply. - ✅ You can catch several types in one block with a tuple, like
except (TypeError, ValueError):, when the response is the same. - ✅ A bare
except:catches everything and hides real bugs, so you should name the exceptions you expect. - ✅ When you need a catch-all,
except Exception:is safer than a bareexcept:.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What happens when you stack multiple except blocks under one try and an error occurs?
Why: Python checks the except blocks top to bottom and runs only the first one that matches the raised error.
- 2
How do you catch both TypeError and ValueError in a single except block?
Why: You list the exception types in a tuple inside round brackets, like except (TypeError, ValueError):.
- 3
Why is a bare except: usually a bad idea?
Why: A bare except catches every error, so genuine bugs get swallowed silently and your program looks fine when it is broken.
- 4
If you want one block to handle a broad range of errors, what should you use instead of a bare except?
Why: except Exception: catches most errors but still lets system signals like Ctrl+C pass through, so it is safer than a bare except.
🚀 What’s Next?
You now know how to react differently to different errors. But what about code that must run no matter what, error or not, like closing a file? That is the job of the next lesson.