Skip to content
Python Basics: Build Focused Error Reports with traceback

Python Basics: Build Focused Error Reports with traceback

October 24, 2026

A command-line tool may need a short validation error, while a diagnostic report needs the failing call path. traceback lets you choose which details to retain and format. These examples intentionally raise and catch local exceptions, producing stable summaries instead of machine-specific absolute paths.

Prepare a scratch directory

These standalone examples were tested on Linux with Python 3.12.14. Use Python 3.10 or newer because format_exception_only accepts an exception directly in the first example. Use Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. No third-party packages are required. Run without -O so the assertions remain enabled.

Create a fresh directory and keep using the same terminal:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Save each named source below as UTF-8. Avoid filenames that shadow standard-library modules.

1. Format an exception without its stack frames

Save as format_validation_error.py:

import traceback

def validate_count(count):
    if count < 0:
        raise ValueError("count must be nonnegative")
    return count

assert validate_count(3) == 3
try:
    validate_count(-2)
except ValueError as error:
    message = "".join(traceback.format_exception_only(error)).strip()
    assert message == "ValueError: count must be nonnegative"
    print(message)
else:
    raise AssertionError("Expected a validation failure")

Run it:

python3 -B format_validation_error.py

Output:

ValueError: count must be nonnegative

format_exception_only returns text pieces containing the exception type and message without the call stack. Join them rather than assuming one item. This works well for an expected validation failure; unexpected failures may need a fuller diagnostic record. See format_exception_only.

2. Extract the innermost part of a call path

Save as summarize_failure_frames.py:

from pathlib import Path
import traceback

def read_count(text):
    return int(text)

def build_summary(text):
    return {"count": read_count(text)}

try:
    build_summary("three")
except ValueError as error:
    frames = traceback.extract_tb(error.__traceback__, limit=-2)
    names = [frame.name for frame in frames]
    assert names == ["build_summary", "read_count"]
    deepest = frames[-1]
    assert deepest.line == "return int(text)"
    print("Call path:", " -> ".join(names))
    print("File:", Path(deepest.filename).name)
    print("Failing source:", deepest.line)
else:
    raise AssertionError("Expected a conversion failure")

Run it:

python3 -B summarize_failure_frames.py

Output:

Call path: build_summary -> read_count
File: summarize_failure_frames.py
Failing source: return int(text)

A negative limit retains the last frames, nearest the failure. FrameSummary exposes structured information, so this report prints a basename rather than an absolute path. Source lines are useful for local diagnosis but can contain private data. See extract_tb.

3. Keep a formatted snapshot without collecting locals

Save as capture_error_snapshot.py:

import traceback

def load_setting():
    internal_note = "example-private-value"
    raise LookupError("setting is missing")

try:
    load_setting()
except LookupError as error:
    snapshot = traceback.TracebackException.from_exception(
        error, capture_locals=False
    )
else:
    raise AssertionError("Expected a missing setting")

# Formatting happens after the exception handler has finished.
report = "".join(snapshot.format())
assert report.startswith("Traceback (most recent call last):")
assert report.rstrip().endswith("LookupError: setting is missing")
assert "example-private-value" not in report
assert all(frame.locals is None for frame in snapshot.stack)
print("Captured frames:", len(snapshot.stack))
print("Locals recorded:", any(frame.locals is not None for frame in snapshot.stack))
print("Final line:", report.splitlines()[-1])

Run it:

python3 -B capture_error_snapshot.py

Output:

Captured frames: 2
Locals recorded: False
Final line: LookupError: setting is missing

TracebackException captures information for later formatting without retaining the original exception and traceback objects. capture_locals=False avoids collecting representations of local variables. The assertion checks this specific fixture, not a general redaction guarantee. See TracebackException.

Error messages, source lines, filenames, chained exceptions, and exception notes may still contain secrets even when local variables are omitted. Decide what a report may include before sending it outside the process. Keep raw diagnostics access-controlled and apply deliberate redaction when needed. Exact traceback presentation varies with Python versions; these tests verify chosen fields and summaries, not the entire interpreter display.

Clean up

In the same terminal, remove only the named sources, return to the original directory, and remove the empty scratch directory:

rm -- format_validation_error.py summarize_failure_frames.py capture_error_snapshot.py
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If rmdir reports remaining files, inspect them instead of deleting unknown contents.

References

Acknowledgements

This post was created with assistance from AI.

Last updated on