Python try and except: Complete Beginner Guide

Published on: July 10, 2026
Reading time: 5 minutes
logotipo do Python em azul e amarelo ao lado das palavras 'try except' em fonte amarela sobre um fundo azul-texturizado

Python try and except statements let a program respond to expected runtime errors instead of stopping abruptly. They are useful when reading files, converting user input, making network requests, accessing dictionaries, or working with resources that may be unavailable.

Good exception handling does not hide every problem. It catches specific failures, provides useful context, recovers only when recovery makes sense, and allows unexpected programming errors to remain visible. This guide covers try, except, else, finally, multiple exception types, raising exceptions, custom exceptions, logging, and practical patterns.

Related guides include Python data types, functions, and logging.

What Is an Exception?

An exception is an object that represents an error detected while a program is running. For example:

number = int("hello")

Python raises ValueError because the text cannot be converted to an integer. Without handling, the program stops and prints a traceback.

The official Python errors and exceptions tutorial explains the language syntax. The built-in exceptions reference describes standard exception classes.

Your First try and except

raw_value = input("Enter an integer: ")

try:
    number = int(raw_value)
except ValueError:
    print("That is not a valid integer.")

Python executes the code inside try. If ValueError occurs, execution moves to the matching except block.

Catch the Exception Object

try:
    number = int("twelve")
except ValueError as error:
    print(f"Conversion failed: {error}")

The exception object contains a message that can help diagnose the failure. Use it carefully in user-facing output because technical messages may reveal internal details.

Catch Specific Exceptions

Avoid a broad except Exception when you know which operation may fail:

from pathlib import Path

path = Path("settings.txt")

try:
    content = path.read_text(encoding="utf-8")
except FileNotFoundError:
    print("The settings file does not exist.")
except PermissionError:
    print("The settings file cannot be read.")
except UnicodeDecodeError:
    print("The file is not valid UTF-8 text.")

Specific handlers allow different recovery decisions. The pathlib guide covers file paths, while the FileNotFoundError guide explains path-related failures.

Catch Multiple Exceptions Together

When several exception types require the same response, place them in a tuple:

try:
    value = int(input("Number: "))
    result = 100 / value
except (ValueError, ZeroDivisionError) as error:
    print(f"The calculation could not be completed: {error}")

Use separate handlers when the user needs different messages or the program needs different recovery logic.

Use else for the Success Path

The else block runs only when no exception occurs:

try:
    age = int(input("Age: "))
except ValueError:
    print("Enter a whole number.")
else:
    print(f"Next year you will be {age + 1}.")

Keeping success code in else narrows the protected area and prevents unrelated errors from being caught accidentally.

Use finally for Cleanup

The finally block runs whether an exception occurs or not:

file = None

try:
    file = open("data.txt", "r", encoding="utf-8")
    content = file.read()
except OSError as error:
    print(f"File operation failed: {error}")
finally:
    if file is not None:
        file.close()

For files, a context manager is normally better:

try:
    with open("data.txt", "r", encoding="utf-8") as file:
        content = file.read()
except OSError as error:
    print(f"File operation failed: {error}")

finally remains important for resources that need explicit cleanup, such as locks, temporary state, or some external connections.

Keep the try Block Small

This block protects too much code:

try:
    number = int(raw_value)
    result = calculate_report(number)
    save_report(result)
except ValueError:
    print("Invalid input")

A ValueError from calculate_report() might be mistaken for invalid user input. A clearer structure is:

try:
    number = int(raw_value)
except ValueError:
    print("Invalid input")
else:
    result = calculate_report(number)
    save_report(result)

Raise an Exception with raise

Your own functions should reject invalid states explicitly:

def calculate_discount(total: float, rate: float) -> float:
    if total < 0:
        raise ValueError("total cannot be negative")
    if not 0 <= rate <= 1:
        raise ValueError("rate must be between 0 and 1")

    return total * rate

Raising a standard exception gives the caller a clear contract and prevents an incorrect result from spreading through the program.

Re-Raise an Exception

Sometimes you need to record context and still allow the failure to continue:

import logging


def load_report(path: str) -> str:
    try:
        with open(path, "r", encoding="utf-8") as file:
            return file.read()
    except OSError:
        logging.exception("Could not load report from %s", path)
        raise

A bare raise inside an exception handler preserves the original traceback.

Chain Exceptions with from

Exception chaining adds a clearer high-level message while preserving the original cause:

def read_port(value: str) -> int:
    try:
        port = int(value)
    except ValueError as error:
        raise ValueError("PORT must be a whole number") from error

    if not 1 <= port <= 65535:
        raise ValueError("PORT must be between 1 and 65535")

    return port

The traceback shows both the conversion failure and the application-specific explanation.

Create a Custom Exception

A custom exception is useful when callers need to distinguish a domain rule from ordinary technical errors:

class InsufficientBalanceError(Exception):
    """Raised when an account cannot complete a withdrawal."""


def withdraw(balance: float, amount: float) -> float:
    if amount <= 0:
        raise ValueError("amount must be positive")
    if amount > balance:
        raise InsufficientBalanceError("insufficient balance")

    return balance - amount

Inherit from Exception and give the class a meaningful name ending in Error.

Validate User Input in a Loop

def read_age() -> int:
    while True:
        try:
            age = int(input("Age: "))
        except ValueError:
            print("Enter a whole number.")
            continue

        if age < 0:
            print("Age cannot be negative.")
            continue

        return age

This function separates conversion errors from business validation. The loop ends only when valid input is returned.

Handle Dictionary Lookups

Use a default when a missing key is normal:

user = {"name": "Nina"}
role = user.get("role", "guest")

Catch KeyError when the key is required and its absence is exceptional:

try:
    email = user["email"]
except KeyError:
    print("The user record has no email field.")

The right approach depends on the data contract, not only on syntax.

Handle Network Requests

import requests

try:
    response = requests.get("https://example.com/api/items", timeout=10)
    response.raise_for_status()
    data = response.json()
except requests.Timeout:
    print("The service took too long to respond.")
except requests.ConnectionError:
    print("Could not connect to the service.")
except requests.HTTPError as error:
    print(f"The server returned an HTTP error: {error}")
except requests.RequestException as error:
    print(f"The request failed: {error}")

The Requests guide explains status codes, timeouts, sessions, and JSON.

Log Unexpected Failures

import logging

logging.basicConfig(level=logging.INFO)

try:
    process_orders()
except OSError:
    logging.exception("Order processing failed because of a system error")

logging.exception() records the traceback when called inside an exception handler. Avoid logging passwords, tokens, or sensitive personal information.

Do Not Silence Errors

This pattern is dangerous:

try:
    process_data()
except Exception:
    pass

It hides every ordinary exception and gives no evidence that processing failed. At minimum, handle a specific exception and make a deliberate recovery decision.

EAFP and LBYL

Python code often follows “easier to ask forgiveness than permission” (EAFP): attempt an operation and handle the expected exception.

try:
    value = settings["theme"]
except KeyError:
    value = "light"

“Look before you leap” (LBYL) checks first:

if "theme" in settings:
    value = settings["theme"]
else:
    value = "light"

Neither style is universally superior. Use the form that is correct, readable, and safe for the operation.

Test Exception Behavior

import pytest


def test_discount_rejects_invalid_rate():
    with pytest.raises(ValueError, match="between 0 and 1"):
        calculate_discount(100, 1.5)

The Pytest guide covers assertions, fixtures, and exception tests.

Common Mistakes

  • Catching Exception around a large block of code.
  • Using an empty except block.
  • Catching an exception and continuing with invalid data.
  • Displaying technical tracebacks directly to end users.
  • Using exceptions for ordinary control flow when a simple check is clearer.
  • Forgetting cleanup when a resource is acquired manually.
  • Raising a generic exception when a standard specific class exists.
  • Losing the original cause instead of chaining exceptions.

Practical Example: Safe Configuration Loader

import json
from pathlib import Path


class ConfigurationError(Exception):
    """Raised when application configuration cannot be loaded."""


def load_configuration(path: Path) -> dict:
    try:
        text = path.read_text(encoding="utf-8")
        data = json.loads(text)
    except FileNotFoundError as error:
        raise ConfigurationError(f"configuration not found: {path}") from error
    except PermissionError as error:
        raise ConfigurationError(f"configuration cannot be read: {path}") from error
    except json.JSONDecodeError as error:
        raise ConfigurationError(f"configuration contains invalid JSON: {path}") from error

    if "database_url" not in data:
        raise ConfigurationError("database_url is required")

    return data

The function translates low-level failures into one application-specific exception while preserving each original cause.

Frequently Asked Questions

Should every function use try and except?

No. Add handlers where the program can provide context, recover, clean up, or translate the error. Let other exceptions propagate to an appropriate boundary.

What is the difference between else and finally?

else runs only after a successful try. finally runs whether the operation succeeds or fails.

Can one except handle several errors?

Yes. Put exception classes in a tuple when they require the same response.

Why should the try block be small?

A small block reduces the chance of catching an exception from the wrong operation.

Should I print or log an exception?

Print may be sufficient for a small command-line exercise. Applications normally benefit from structured logging and a separate user-friendly message.

Conclusion

Python try and except statements make programs more dependable when used deliberately. Protect the operation that may fail, catch specific exceptions, keep the success path in else, and use finally for unavoidable cleanup.

Raise clear exceptions when your own function receives invalid data, preserve causes with chaining, and test failure paths. Good exception handling does not make errors disappear—it makes them understandable and manageable.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Uso de f-strings para debug e formatação em Python
    Error Resolution
    Foto de perfil de Leandro Hirt da Academify

    Debug Python with f-Strings: Practical Guide

    Learn to debug Python with f-strings using the debug specifier, repr, type checks, number formatting, loops, functions, exceptions, logging, and

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Como resolver UnicodeDecodeError em arquivos Python
    Error Resolution
    Foto de perfil de Leandro Hirt da Academify

    Fix Python UnicodeDecodeError Simply

    Fix Python UnicodeDecodeError: always specify encoding, use chardet to auto-detect, handle CSV files with Pandas, and apply try/except fallback.

    Ler mais

    Tempo de leitura: 3 minutos
    03/06/2026
    Como resolver problemas de caminhos de arquivos em scripts Python
    Error Resolution
    Foto de perfil de Leandro Hirt da Academify

    Fix Python FileNotFoundError: Path Guide

    Fix Python FileNotFoundError: understand absolute vs relative paths, use pathlib and os.getcwd(), handle errors with try/except, and build a reliable

    Ler mais

    Tempo de leitura: 3 minutos
    03/06/2026
    Como resolver erros de codificação UTF-8 em Python
    Error Resolution
    Foto de perfil de Leandro Hirt da Academify

    Fix Python UTF-8 Encoding Errors

    Fix Python UTF-8 encoding errors: always specify encoding when opening files, handle UnicodeDecodeError, auto-detect with chardet, and write a safe

    Ler mais

    Tempo de leitura: 4 minutos
    03/06/2026
    Como corrigir erro ModuleNotFoundError em Python rapidamente
    Error Resolution
    Foto de perfil de Leandro Hirt da Academify

    Fix Python ModuleNotFoundError Fast

    Fix Python ModuleNotFoundError: check missing packages, typos, virtual environments, sys.path issues, and file name conflicts with practical examples.

    Ler mais

    Tempo de leitura: 8 minutos
    03/06/2026
    Como corrigir ImportError em projetos Python rapidamente
    Error Resolution
    Foto de perfil de Leandro Hirt da Academify

    Fix Python ImportError in 2 Minutes

    Fix Python ImportError in 2 minutes: identify duplicate file names, circular imports, missing names, and use a diagnostic script to

    Ler mais

    Tempo de leitura: 4 minutos
    03/06/2026