Skip to Content
Python try/except: Catch the Error You Expect, Not Every Error
Beginner
0 likes
Tutorials

Python try/except: Catch the Error You Expect, Not Every Error

A bare except hides typos and real bugs behind wrong results. Catch the specific error you expect, keep the try block small, and let everything else crash where you can see it.

Catch the error you expect, in the smallest piece of code that can raise it, and let everything else crash. That sounds harsh. But a crash with a clear traceback is easy to fix. An error that was silently swallowed can take days to find, because nothing tells you it happened.

Skip ahead: If you know try/except already, go to "Why except: on its own is dangerous". That's the part people get wrong.

The basic shape

text = "12x"
try:
    quantity = int(text)
except ValueError:
    print(f"{text!r} is not a whole number")
'12x' is not a whole number

Python runs the try block. If int() raises a ValueError, it jumps to the matching except block instead of crashing. If anything else goes wrong, it's not caught, and you get the normal traceback.

How do you know which error to catch? Make it happen once on purpose and read the last line of the traceback. ValueError: invalid literal for int()... tells you the name: ValueError.

Why except: on its own is dangerous

This version looks safer, because it catches "everything":

prices = {"coffee": 95, "bread": 35}


def price_of(item):
    try:
        return prices[itme]
    except:
        return 0


print(price_of("coffee"))
0

Coffee costs 0. There's a typo, itme instead of item, which raises a NameError. The bare except: caught it and returned 0 as if the item simply wasn't in the list. The bug is now invisible.

Catch only the error you mean:

prices = {"coffee": 95, "bread": 35}


def price_of(item):
    try:
        return prices[itme]
    except KeyError:
        return 0


print(price_of("coffee"))
Traceback (most recent call last):
  File "/home/you/project/specific.py", line 11, in <module>
    print(price_of("coffee"))
          ~~~~~~~~^^^^^^^^^^
  File "/home/you/project/specific.py", line 6, in price_of
    return prices[itme]
                  ^^^^
NameError: name 'itme' is not defined

Now the typo crashes immediately, with the exact line. Fix it, and the KeyError handling still works for items that really aren't on the list.

⚠ Warning: A bare except: also catches KeyboardInterrupt, which is what Ctrl+C raises. A loop with a bare except: inside can refuse to stop when you press Ctrl+C. except Exception: doesn't have that problem, but it still hides typos and bugs. Use it only at the very top of a program, to log the error before exiting, never to keep going as if nothing happened.

Keep the try block small

The less code inside try, the more sure you are where the error came from:

def read_quantity(row):
    try:
        quantity = int(row["qty"])
    except ValueError:
        print(f"Bad quantity {row['qty']!r}, skipping")
        return None
    return quantity * row["price"]


print(read_quantity({"qty": "3", "price": 35}))
print(read_quantity({"qty": "three", "price": 35}))
105
Bad quantity 'three', skipping
None

Only the conversion is inside try. If the multiplication on the last line fails, that's a different problem, and it should crash with its own error, not print "Bad quantity".

Catching more than one error

When two different errors mean the same thing to you, catch them together. When they need different handling, use separate blocks:

def load_setting(settings, name):
    try:
        return int(settings[name])
    except KeyError:
        print(f"{name} is missing, using 10")
        return 10
    except (ValueError, TypeError):
        print(f"{name} is not a number: {settings[name]!r}")
        raise


settings = {"timeout": "30", "retries": "three"}
print(load_setting(settings, "timeout"))
print(load_setting(settings, "max_items"))
try:
    load_setting(settings, "retries")
except ValueError as error:
    print("Stopped:", error)
30
max_items is missing, using 10
10
retries is not a number: 'three'
Stopped: invalid literal for int() with base 10: 'three'

Two things here. A missing setting has a sensible default, so that case recovers. A setting that exists but is wrong has no sensible default, so the function prints what it found and then uses raise on its own, which re-raises the same error. Guessing a value for a broken setting is worse than stopping.

else and finally

def safe_divide(a, b):
    try:
        result = a / b
    except ZeroDivisionError:
        print("Can't divide by zero")
        return None
    else:
        print("Division worked")
        return result
    finally:
        print("This runs every time")


print(safe_divide(10, 2))
print(safe_divide(10, 0))
Division worked
This runs every time
5.0
Can't divide by zero
This runs every time
None
  • **else** runs only when the try block raised nothing. It keeps the success code out of try, so its errors aren't caught by mistake.
  • **finally** runs no matter what, even after return. Use it for clean-up. For files, with open(...) already does the clean-up for you, so you'll need finally less than you think.

Should you check first instead?

Sometimes an if is clearer than try. if item in prices: reads fine. But for things that can change between the check and the action, like a file that exists now and is deleted a second later, or text that might not convert, trying and catching the specific error is more reliable. Both styles are fine. Pick the one that says what you mean.

Common mistakes

  • **Bare except: or except Exception: to "be safe".** It hides bugs. Name the error.
  • **except: pass.** The error is gone, and so is every clue.
  • **A big try block around twenty lines.** You don't know which line failed.
  • Printing the error and continuing with a missing value. If you can't recover, re-raise or stop.

What happens if you catch everything

The program never crashes. It just produces wrong results, and there's no traceback to tell you where. In the price example, every item with a typo in its code costs zero. The first person to notice is the one checking the sales total.

If your script ran overnight and caught 300 errors silently, how would you know in the morning?

About

Catching named exceptions, why bare except is dangerous, small try blocks, re-raising, and else and finally. After this, your error handling recovers from what it should and stops hiding what it shouldn't.

Responsible
Christopher R
Last Update
09/12/2026
Reading Time
5 min
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.