---
title: Try/Except and Exception Handling in Python
source: https://app.sythra.ai/learn/python/try-except-python
topic: Python
updated: 2026-08-12
publisher: Sythra (https://app.sythra.ai)
---

# Try/Except and Exception Handling in Python

try/except in Python lets a program catch errors like ZeroDivisionError or ValueError instead of crashing — wrap risky code in try, handle specific errors in except, use else for success-only code, and finally for cleanup that always runs.

_Source: [https://app.sythra.ai/learn/python/try-except-python](https://app.sythra.ai/learn/python/try-except-python) — free to read on Sythra._

## Key points

- An exception is Python's way of signaling an error it cannot recover from automatically
- Catch specific exception types (except ValueError:) instead of a bare except:
- except (TypeA, TypeB): catches multiple exception types with one block
- except SomeError as e: lets you read the actual error message
- else runs only if try succeeded; finally always runs, error or not
- raise lets you trigger your own exceptions, including custom classes that inherit from Exception

Imagine a program that asks the user to type a number, then divides 10 by it. If the user types `0`, the program explodes with a scary red error message and stops dead. If they type `"banana"` instead of a number, it explodes again, in a different way. **Try/except and exception handling in Python** is how you teach a program to expect these problems, catch them, and react calmly instead of crashing.

Until now, every error you've hit has been a bug — something _you_ did wrong, fixed by rewriting your code. This page is about a different kind of problem: errors caused by things **outside your control** — bad user input, a missing file, a network that doesn't respond.

## What you will learn

- What an exception actually is, and how to read a traceback
- The `try`/`except` statement, and why bare `except:` is risky
- Catching specific exception types, and multiple types together
- Getting the error message with `as e`
- The `else` and `finally` clauses, and the order they run in
- Raising your own exceptions with `raise`, including custom exception classes

## What is an exception?

When Python runs into a problem it cannot recover from on its own, it doesn't just quietly fail — it **raises an exception**. Think of an exception like a small alarm bell going off inside your program. The moment that alarm rings, Python stops whatever it was doing and looks for someone to "catch" it. If nobody catches it, the alarm reaches all the way to the top, and your whole program crashes with a wall of red text.

```python
x = 10 / 0
# ZeroDivisionError: division by zero
```

That whole message is called a **traceback** — Python's way of saying "here is exactly where the alarm went off, and here is what kind of alarm it was." The last line tells you two things: the **type of exception** (`ZeroDivisionError`) and the **error message** (`division by zero`).

```python
age = int("hello")
# ValueError: invalid literal for int() with base 10: 'hello'
```

Python tried to turn the text `"hello"` into a number, couldn't do it, and rang the alarm bell — this time the exception type is `ValueError`. See [Type Casting in Python](/learn/python/type-casting) for more on why casting can fail.

## Why letting your program crash is a problem

Imagine you built a simple calculator for a friend, and somewhere in the middle of using it, they accidentally divide by zero. Without any protection, the _entire program_ shuts down immediately — even if they were about to do five more calculations after that. One small mistake takes down everything. What we want instead is a program that says: "Oh, that didn't work. Let me tell the user nicely, and keep going as if nothing happened." That's exactly what `try` and `except` let you do.

## The try/except statement: catching the alarm

```python
try:
    risky_code_that_might_fail()
except:
    print("Something went wrong!")
```

- `try:` — "attempt to run the code in this block; I know it might fail, so be careful"
- The indented lines under `try` are the code you suspect could cause an exception
- `except:` — "if anything inside that try block rings the alarm bell, come here instead of crashing the whole program"
- The indented lines under `except` run **only if** something went wrong above

```python
try:
    number = int(input("Enter a number: "))
    result = 10 / number
    print("10 divided by your number is:", result)
except:
    print("That didn't work. Please try a valid, non-zero number next time.")
```

If the user types `5`, the `try` block runs perfectly and the `except` block is completely skipped. If they type `0`, Python hits `10 / 0`, the alarm bell rings (`ZeroDivisionError`), and Python jumps into the `except` block instead of crashing. If they type `banana`, the alarm rings even earlier, at `int(input(...))` (`ValueError`), and Python jumps to the same `except` block. This is the entire magic trick of exception handling: **wrap the risky part in `try`, and put your "Plan B" in `except`.**

## Catching specific exceptions

A bare `except:` catches _any_ kind of error — it's like a smoke detector that goes off for smoke, dust, steam, and burnt toast, and you can never tell which one it actually was. It can also accidentally hide real bugs, because it catches everything, even mistakes you didn't expect. A much better habit is to catch the **specific** type of exception you expect:

```python
try:
    number = int(input("Enter a number: "))
    result = 10 / number
    print("10 divided by your number is:", result)
except ZeroDivisionError:
    print("You can't divide by zero!")
except ValueError:
    print("That wasn't a valid number.")
```

Python checks each `except` line in order, just like `elif`. If a `ZeroDivisionError` happens, only the first block runs. If a `ValueError` happens, only the second block runs — your error messages stay accurate and specific.

## Catching multiple exception types together

Sometimes you want the same response for more than one kind of error. Group the exception types together using parentheses:

```python
try:
    number = int(input("Enter a number: "))
    result = 10 / number
except (ZeroDivisionError, ValueError):
    print("Please enter a non-zero number.")
```

This says: "if either a `ZeroDivisionError` OR a `ValueError` happens, run this same block." The parentheses are required.

## Getting details about the exception with as

Sometimes you don't just want to know _that_ something went wrong — you want to know exactly _what_ the error message said. Grab the exception object with `as`:

```python
try:
    number = int(input("Enter a number: "))
    result = 10 / number
except ZeroDivisionError as e:
    print("Error occurred:", e)
except ValueError as e:
    print("Error occurred:", e)
```

`e` is just a variable name (the common convention for "error") holding the actual exception object — printing it shows the descriptive message that came with it, like `division by zero`. This is incredibly useful while debugging, since you get the real, specific reason instead of a generic "something went wrong."

## The else clause: code that runs only if nothing failed

`try`/`except` has an optional third part: `else`. This block runs **only if the `try` block completed with no exceptions at all.**

```python
try:
    number = int(input("Enter a number: "))
    result = 10 / number
except ZeroDivisionError:
    print("You can't divide by zero!")
except ValueError:
    print("That wasn't a valid number.")
else:
    print("Success! The result is:", result)
```

Why not just put `print("Success!...")` at the bottom of the `try` block instead? Because `else` makes your intentions crystal clear: "this part only happens when everything in `try` went perfectly, with zero surprises." It keeps your "happy path" separated from your risky code, making bugs easier to spot later.

## The finally clause: code that always runs, no matter what

Code inside a `finally` block runs **no matter what happens** — whether the `try` block succeeded, whether an exception was caught, or even if an exception happened that _wasn't_ caught at all.

```python
try:
    file = open("notes.txt", "r")
    content = file.read()
    print(content)
except FileNotFoundError:
    print("That file doesn't exist.")
finally:
    print("Done attempting to read the file.")
```

`finally` is perfect for **cleanup work** — things that absolutely must happen regardless of success or failure, like closing a file you opened. See [File Handling in Python](/learn/python/file-handling-python) for the full picture.

Here's the full order of operations:

```python
try:
    print("1. Trying...")
    x = 10 / 0
except ZeroDivisionError:
    print("2. Caught the error!")
else:
    print("This would only print if there was NO error")
finally:
    print("3. This always runs, error or not.")

# Output:
# 1. Trying...
# 2. Caught the error!
# 3. This always runs, error or not.
```

## Raising your own exceptions

So far, Python has been ringing all the alarm bells automatically. Sometimes _you_ want to ring the alarm yourself, on purpose, because **you** know something is wrong even if Python doesn't. Do this with `raise`:

```python
def set_age(age):
    if age < 0:
        raise ValueError("Age cannot be negative.")
    print("Age set to", age)

set_age(-5)
# ValueError: Age cannot be negative.
```

This is incredibly useful for protecting your own functions — you don't have to wait for Python to naturally stumble into a problem. Whoever calls your function later can wrap it in their own `try`/`except` and handle your raised exception just like any built-in one:

```python
try:
    set_age(-5)
except ValueError as e:
    print("Couldn't set age:", e)
```

## Creating your own custom exception types

Python lets you invent your own exception types, useful when none of the built-ins really describe your specific problem. Create one by making a new class that inherits from `Exception`:

```python
class NegativeAgeError(Exception):
    pass

def set_age(age):
    if age < 0:
        raise NegativeAgeError("Age cannot be negative.")
    print("Age set to", age)
```

That little three-line recipe — `class SomeName(Exception): pass` — is the standard way to create a brand-new, custom-named exception (full detail on classes in [Classes and Objects in Python](/learn/python/classes-and-objects-python)). Once made, you raise and catch it exactly like a built-in exception:

```python
try:
    set_age(-5)
except NegativeAgeError as e:
    print("Custom error caught:", e)
```

`NegativeAgeError` is far more descriptive than a generic `ValueError` — anyone reading your code immediately understands exactly what kind of problem occurred, just from the name.

## Common built-in exceptions you will meet often

| Exception | When it happens |
| --- | --- |
| ZeroDivisionError | Dividing by zero |
| ValueError | Right type, but an invalid value — e.g. int("banana") |
| TypeError | Wrong type entirely — e.g. "2" + 2 |
| NameError | Using a variable that doesn't exist |
| IndexError | Accessing a list position that doesn't exist |
| KeyError | Accessing a dictionary key that doesn't exist |
| FileNotFoundError | Trying to open a file that isn't there |
| AttributeError | Using a method/property that doesn't exist on an object |

You don't need to memorize all of these right now — just know they exist, and when you see one in a traceback, you'll already have a head start on understanding what went wrong.

## Best practices: how to use try/except wisely

- **Don't catch everything blindly.** A bare `except:` hides real bugs along with the errors you actually expected. Catch specific exception types whenever you can.
- **Only wrap the risky line(s), not your whole program.** Keep the `try` block small and focused so it's obvious exactly what could fail.
- **Use `finally` for cleanup** — closing files or releasing resources, things that must happen either way.
- **Don't use exceptions to replace normal `if` checks.** If you can easily check for a problem in advance, do that first. Exceptions are best for situations you genuinely can't predict, like bad user input or a missing file.
- **Give your `except` blocks something to actually do.** An empty `except: pass` silently swallows errors and makes bugs nearly impossible to track down later.

> **Practice with Sythra:**  [Practice with AI tutor](https://app.sythra.ai/pricing)

## FAQ

### What is an exception in Python?

An exception is Python's way of signaling that something went wrong that it cannot recover from automatically, such as dividing by zero (ZeroDivisionError) or converting invalid text to a number (ValueError). Uncaught exceptions crash the program.

### What is the difference between try, except, else, and finally?

try holds code that might fail. except catches and handles specific exceptions if they occur. else runs only if the try block succeeded with no exceptions. finally always runs, whether or not an exception happened, and is used for cleanup.

### Why shouldn't I use a bare except: in Python?

A bare except: catches every possible error, including ones you didn't anticipate, which can hide real bugs. Catching specific exception types like except ValueError: keeps error handling accurate and makes unexpected bugs visible instead of silently swallowed.

### How do you raise a custom exception in Python?

Use the raise keyword with a built-in exception like raise ValueError("message"), or define your own by creating a class that inherits from Exception, like class NegativeAgeError(Exception): pass, then raise NegativeAgeError("message").

### How do I see the actual error message in an except block?

Use the as keyword to capture the exception object, like except ValueError as e:, then print(e) to see the specific message Python attached to that error.

## Related

- [Type Casting in Python](https://app.sythra.ai/learn/python/type-casting) — Why int() and float() can raise ValueError.
- [Conditionals in Python (if, elif, else)](https://app.sythra.ai/learn/python/conditionals-in-python) — When to use if checks instead of exceptions.
- [The match-case Statement in Python](https://app.sythra.ai/learn/python/match-case-python) — Default cases — the same safety-net idea as except.
- [Python course hub](https://app.sythra.ai/learn/python) — All free Python explainers and the path into Agentic practice.
- [File Handling in Python (Reading, Writing, CSV & JSON)](https://app.sythra.ai/learn/python/file-handling-python) — open(), with, reading modes, CSV, and JSON.

---

Written by Sythra — Learn machine learning by building. Practice this topic with Sythra's AI tutor: https://app.sythra.ai/pricing
