f in x
Python Error Handling — try except finally and logging for Robust Code
> cd .. / HUB_EDITORIALE
Sviluppo di siti web

Python Error Handling — try except finally and logging for Robust Code

[2026-06-21] Author: Ing. Calogero Bono

Ever seen a Python application freeze because a file didn't exist or an API returned a 500 error? Happens to everyone. And when it happens in production, the user sees a blank screen or a cryptic error. We, at Meteora Web, have seen this dozens of times in projects that come to us: code that works locally but explodes in production. The difference? Solid error handling: try, except, finally, and logging. It's not boring theory: it's the difference between an application that recovers and one that shuts down. In this guide, we show you how to implement it.

Why is error handling crucial for robust Python code?

When an exception is not caught, Python stops the program. In a web app that means a 500 Internal Server Error; in a batch script it means a failed process with no warning. One client we worked with (a clothing e-commerce whose ERP we managed) had an order import module that crashed on a single malformed product. Result: hundreds of unprocessed orders. We added try/except with logging to skip the problematic record and continue importing, logging the error for manual review. The process never stopped again.

Sponsored Protocol

Error handling is not just about preventing crashes: it makes the program predictable. The user must know something went wrong, but the system must not go down. And you, as a developer, must be able to understand what happened. That's why logging is not optional.

How do try, except and finally work in Python?

The try block contains code that might fail. After it, one or more except blocks catch specific exceptions. The finally block always runs, whether an exception occurred or not, and is ideal for releasing resources (closing files, connections).

try:
    file = open('data.txt', 'r')
    content = file.read()
    number = int(content.strip())
except FileNotFoundError:
    print('File not found. Using default value.')
    number = 0
except ValueError:
    print('File content is not a valid integer.')
    number = 0
finally:
    # Close file even if exception occurred
    try:
        file.close()
    except NameError:
        pass  # file was never opened

Note the specific except FileNotFoundError instead of a generic except:. Catching everything hides bugs. When the file doesn't exist, finally tries to close but if opening failed the variable file doesn't exist: that's why we handle NameError with a second try/except. Real-world scenario.

Sponsored Protocol

The optional else block runs only if no exception was raised. Useful for code that must execute only on success:

try:
    result = risky_operation()
except ValueError:
    print('Operation failed')
else:
    print('Operation succeeded, value:', result)

Which exceptions should you catch and how to organize them without hiding bugs?

The golden rule: catch specific exceptions, not generic ones. An except Exception catches everything, including KeyboardInterrupt and SystemExit (though they don't actually inherit from Exception, many mistakenly use a bare except:). Instead, use documented types: ValueError for invalid input, TypeError for incompatible type operations, FileNotFoundError, ConnectionError, etc.

If you have multiple exceptions to handle the same way, group them in a tuple:

try:
    value = int(input('Enter a number: '))
    result = 10 / value
except (ValueError, ZeroDivisionError) as e:
    print(f'Input or division error: {e}')

A common mistake is catching and moving on without leaving any trace. We often see code like:

Sponsored Protocol

try:
    do_something()
except:
    pass  # do nothing

This is extremely dangerous: the error is swallowed, and no one will ever know. The system might appear to work, but with corrupted or incomplete data. Always log, at least at WARNING or ERROR level.

How to integrate logging to track errors effectively?

Python's logging module is the standard tool for tracking events. It's more powerful than print because you can configure formats, levels (DEBUG, INFO, WARNING, ERROR, CRITICAL), and destinations (file, console, email).

Basic setup at program startup:

import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('app.log', encoding='utf-8'),
        logging.StreamHandler()   # also to console
    ]
)

Then, inside except blocks use logging.error() or logging.exception() to automatically include the traceback:

try:
    with open('config.json') as f:
        config = json.load(f)
except FileNotFoundError:
    logging.warning('Config file not found, using defaults')
    config = {}
except json.JSONDecodeError as e:
    logging.error('JSON parsing error: %s', e)
    raise  # re-raise if you can't recover

logging.exception() logs the message and the traceback, but must be called only inside an except. Great for debugging.

Sponsored Protocol

We, at Meteora Web, have structured logging in all our Laravel and Python applications to have a chronological history of errors on file. When a server's automatic SSL certificate renewal broke, the logs let us identify the issue in minutes, without taking the client offline.

What common mistakes to avoid in exception handling?

Here are some anti-patterns we see often:

  • Too generic: except: or except Exception: without reason.
  • Swallowing exceptions that should propagate: unless you can truly recover, re-raise with raise.
  • Not using finally: if you open a file or connection, always close it even on error. with statement helps, but for custom resources use finally.
  • Mixing logging and print: choose logging, configured for production (e.g., only ERROR to file).
  • Ignoring built-in exceptions: StopIteration, KeyboardInterrupt should not be caught unless for specific purposes.

What to do now

Don't wait for a production error to wake you up at night. Here are three concrete actions to take today:

Sponsored Protocol

  1. Analyze your current code: look for blocks that can raise exceptions (divisions, file access, API calls, parsing) and verify they have try/except with specific exception type.
  2. Replace print with logging: configure a logger with a file and appropriate levels. For example, DEBUG during development, ERROR in production.
  3. Add a handler for uncaught exceptions: use sys.excepthook to log them:
import sys
import logging

def handle_uncaught_exception(exc_type, exc_value, exc_traceback):
    logging.critical("Unhandled exception", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = handle_uncaught_exception

This is your safety net. If something slips through, at least you'll have a record.

This guide is a deep dive into a specific topic of our Python for Developers Pillar, where you can find the complete path. For official documentation: Errors and Exceptions and Logging.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()