When an exception is not handled, Python prints a sequence of files, lines, and function calls that led to the failure. That sequence is a traceback. The Python traceback module lets applications capture, format, limit, store, and display this information in a controlled way, making it useful for command-line tools, APIs, background workers, asynchronous tasks, logging systems, and debugging utilities.
This guide covers print_exc(), format_exc(), extract_tb(), TracebackException, StackSummary, and clear_frames(). It complements our articles about debugging with pdb, object introspection with inspect, Python memory problems, and PermissionError.
What a traceback contains
An exception stores a reference to its traceback object in __traceback__. Each traceback entry represents a frame in the call stack and includes the source filename, line number, function name, and source-code context.
def divide(a, b):
return a / b
def run():
return divide(10, 0)
try:
run()
except ZeroDivisionError as error:
print(type(error.__traceback__))Frames form a chain through tb_next. Applications rarely need to traverse that chain manually because the module provides higher-level extraction and formatting APIs.
Print the current exception with print_exc()
Inside an except block, traceback.print_exc() produces output similar to the interpreter’s unhandled-exception display.
import traceback
try:
run()
except Exception:
traceback.print_exc()The default destination is sys.stderr. A file or compatible stream may be supplied explicitly:
import sys
try:
run()
except Exception:
traceback.print_exc(file=sys.stdout)This is convenient in interactive tools. Production applications should generally integrate errors with structured logging and monitoring rather than printing them without context.
Return a traceback as text
format_exc() returns one string instead of writing immediately.
try:
run()
except Exception:
text = traceback.format_exc()
send_to_monitoring(text)The result can be placed in an internal report, attached to a job record, or stored temporarily for administrators. Do not expose full tracebacks to end users because filenames, internal function names, database statements, and other sensitive details may appear.
Use logging.exception()
The standard logging module already knows how to include the active traceback. Inside an exception handler, logger.exception() writes a message and the current exception information.
import logging
logger = logging.getLogger(__name__)
try:
run()
except Exception:
logger.exception("Operation failed")The official logging documentation explains handlers, formatters, filters, and levels. Avoid logging the same exception at every architectural layer; duplicate records distort error rates and waste storage.
Limit the number of frames
Printing and formatting functions accept a limit argument. Positive and negative limits select different ends of the traceback according to the API.
try:
run()
except Exception:
traceback.print_exc(limit=-3)The final frames are usually closest to the failing instruction. Removing too much context, however, may hide the request handler, job entry point, or plugin that initiated the call. Choose a limit appropriate to the environment and retain a complete internal version when necessary.
Format only the exception
When the call stack is unnecessary, format_exception_only() returns the exception type and message.
try:
run()
except Exception as error:
lines = traceback.format_exception_only(error)
message = "".join(lines)
print(message)Syntax errors receive extra source-position information. Notes added through BaseException.add_note() are also included by current Python versions.
Extract structured frame data
extract_tb() converts a traceback into a StackSummary containing FrameSummary objects.
try:
run()
except Exception as error:
summary = traceback.extract_tb(error.__traceback__)
for frame in summary:
print(frame.filename, frame.lineno, frame.name, frame.line)This representation is useful for JSON output, grouping incidents by file and line, creating fingerprints, or removing private directory prefixes before sending data to an external service.
Capture a stack without an exception
extract_stack() and format_stack() inspect the current call stack. They can reveal who invoked a sensitive operation or provide diagnostics when a task appears stuck.
def sensitive_function():
stack = traceback.extract_stack(limit=-5)
for frame in stack:
print(frame.name, frame.lineno)Stack capture is not free. Avoid collecting it for every successful request in a high-throughput service. Use sampling, debug modes, or clearly defined abnormal conditions.
Store diagnostics with TracebackException
Keeping an exception object can retain its frames and every object reachable from local variables. TracebackException captures enough information for later formatting without retaining the full live object graph.
from traceback import TracebackException
try:
run()
except Exception as error:
captured = TracebackException.from_exception(
error,
limit=-10,
capture_locals=False,
compact=True,
)
text = "".join(captured.format())The official traceback documentation describes this class as the flexible option for deferred rendering and improved memory management.
Be careful with capture_locals
Setting capture_locals=True stores string representations of local variables for every selected frame.
captured = TracebackException.from_exception(
error,
capture_locals=True,
)This can reveal passwords, access tokens, personal information, request bodies, and private keys. It can also produce very large records. Enable it only in controlled environments, redact known secrets, restrict access, and impose size limits.
Chained exceptions
When a new exception occurs during the handling of another, Python stores the original in __context__. Using raise NewError() from original creates an explicit cause through __cause__.
try:
int("abc")
except ValueError as original:
raise RuntimeError("Invalid configuration") from originalTraceback formatters include the chain when chain=True, which is the default. The chain preserves the low-level failure while allowing an application to add a domain-specific explanation.
Exception groups
Concurrent operations may produce an ExceptionGroup containing several failures. TracebackException exposes nested exceptions and supports width and depth limits.
captured = TracebackException.from_exception(
error,
max_group_width=8,
max_group_depth=4,
)These limits prevent one failed batch from producing an enormous report containing hundreds of nearly identical exceptions.
Clear live frame references
When code works directly with a traceback object, clear_frames() clears local variables from all associated frames.
try:
run()
except Exception as error:
tb = error.__traceback__
try:
process(traceback.extract_tb(tb))
finally:
traceback.clear_frames(tb)This reduces accidental memory retention in long-running workers. For diagnostics that must be kept, converting to TracebackException remains the safer default.
Sanitize paths and messages
A traceback may reveal server directories, usernames, source layout, arguments, and code fragments. Before exporting it, remove private prefixes and apply a data-handling policy.
from pathlib import Path
def public_frame(frame):
return {
"file": Path(frame.filename).name,
"line": frame.lineno,
"function": frame.name,
}End users should receive a concise message and an incident identifier. Full diagnostics should remain in access-controlled logs.
Customize StackSummary output
StackSummary can be subclassed and its format_frame_summary() method overridden to omit framework internals, normalize paths, or add organization-specific formatting.
Customization should not remove the first useful application frame or the location that actually raised the exception. Test the formatter against nested calls, recursion, missing source files, and generated code.
Tracebacks are not a complete debugger
A traceback explains how control reached a failure, but it does not always explain why state became invalid. Combine it with structured events, metrics, reproducible tests, and pdb. Native crashes, deadlocks, or interpreter faults may require faulthandler, operating-system diagnostics, or profiler data.
Common mistakes
- Calling
format_exc()outside an active exception handler. - Displaying complete tracebacks to web visitors.
- Capturing local variables that contain secrets.
- Keeping exception and frame objects indefinitely.
- Logging the same failure at every layer.
- Removing so many frames that the entry point disappears.
- Reporting expected validation errors as critical incidents.
- Discarding exception chains.
Best practices
- Use
logger.exception()within the handling layer responsible for the incident. - Convert to
TracebackExceptionfor deferred storage. - Keep
capture_localsdisabled by default. - Redact paths and sensitive values before export.
- Limit very large stacks and exception groups.
- Clear frames when manipulating live traceback objects.
- Return correlation IDs to users.
- Test formatting with chains, groups, and syntax errors.
Conclusion
The Python traceback module turns exception call stacks into information that can be printed, formatted, filtered, and stored. Module-level helpers solve immediate diagnostic needs, while TracebackException, StackSummary, and FrameSummary support structured and persistent error systems.
Safe use requires a balance: preserve enough context to investigate an incident without leaking private data or keeping a large live object graph in memory. With centralized logging, sanitization, sensible limits, and frame cleanup, tracebacks become dependable operational data rather than merely red text in a terminal.







