The simplest way to print a stack trace in Python without crashing is traceback.print_exc() inside an except block. It prints the full traceback, exactly as an uncaught error would, and your program keeps running:
import traceback
try:
risky()
except Exception:
traceback.print_exc()
Each traceback below is real output from Python 3.12.5, captured in Windows Command Prompt.
I’ll show print_exc(), getting the trace as a string, formatting a saved exception, logging it properly, printing the current stack without an error, and chained exceptions.
How to print a stack trace with traceback.print_exc()
print_exc() reads the exception currently being handled and writes its traceback to standard error. Here a bad price string fails two calls deep:
import traceback
def load_price(row):
return float(row["price"])
def total(rows):
return sum(load_price(r) for r in rows)
orders = [{"price": "19.99"}, {"price": "N/A"}]
try:
print(total(orders))
except ValueError:
traceback.print_exc() # full traceback, and the program keeps going
print("still running")
Output:
Traceback (most recent call last):
File "C:\pyguides\trace_print_exc.py", line 11, in <module>
print(total(orders))
^^^^^^^^^^^^^
File "C:\pyguides\trace_print_exc.py", line 7, in total
return sum(load_price(r) for r in rows)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\pyguides\trace_print_exc.py", line 7, in <genexpr>
return sum(load_price(r) for r in rows)
^^^^^^^^^^^^^
File "C:\pyguides\trace_print_exc.py", line 4, in load_price
return float(row["price"])
^^^^^^^^^^^^^^^^^^^
ValueError: could not convert string to float: 'N/A'
still running

total() down to load_price(), and “still running” proves the program carried on.Read it bottom-up: the last line is the error, and the frames above show how you got there. Types of errors in Python walks through reading tracebacks in more detail.
Getting the traceback as a string with format_exc()
traceback.format_exc() returns the same text instead of printing it. That’s what you want for emails, error reports or a database column:
import traceback
try:
{"TX": 0.0625}["NY"]
except KeyError:
details = traceback.format_exc() # the same text, as a string
print(type(details).__name__, len(details.splitlines()), "lines")
print(details.splitlines()[-1]) # just the last line
Output:
str 5 lines
KeyError: 'NY'
Grab just the last line when you only need a short summary, like in a status message.
How do you print the traceback from an exception object?
Sometimes you have the exception saved in a variable and want its trace later. Since Python 3.10, traceback.format_exception() accepts the exception by itself:
import traceback
def parse_qty(text):
return int(text)
try:
parse_qty("two")
except ValueError as err:
lines = traceback.format_exception(err) # Python 3.10+: pass just the exception
print("".join(lines), end="")
frame = traceback.extract_tb(err.__traceback__)[-1]
print(f"failed in {frame.name}() on line {frame.lineno}")
Output:
Traceback (most recent call last):
File "C:\pyguides\trace_exception.py", line 7, in <module>
parse_qty("two")
File "C:\pyguides\trace_exception.py", line 4, in parse_qty
return int(text)
^^^^^^^^^
ValueError: invalid literal for int() with base 10: 'two'
failed in parse_qty() on line 4
extract_tb() turns the traceback into frame objects, so you can pull out the function name, file and line number programmatically.
Logging a stack trace with logging.exception()
In real applications I send tracebacks to the logs rather than the console. log.exception() logs at ERROR level and appends the traceback automatically:
import logging
logging.basicConfig(format="%(levelname)s %(name)s: %(message)s", level=logging.INFO)
log = logging.getLogger("orders")
def charge(amount):
if amount <= 0:
raise ValueError(f"invalid amount ${amount:.2f}")
return f"charged ${amount:.2f}"
for amount in (24.99, 0):
try:
log.info(charge(amount))
except ValueError:
log.exception("charge failed") # logs at ERROR level with the traceback
Output:
INFO orders: charged $24.99
ERROR orders: charge failed
Traceback (most recent call last):
File "C:\pyguides\trace_logging.py", line 13, in <module>
log.info(charge(amount))
^^^^^^^^^^^^^^
File "C:\pyguides\trace_logging.py", line 8, in charge
raise ValueError(f"invalid amount ${amount:.2f}")
ValueError: invalid amount $0.00

Don’t call it outside an except block. For other levels, pass exc_info=True, such as log.warning("retrying", exc_info=True). More on handling errors in catching multiple exceptions.
Printing the current call stack without an exception
traceback.print_stack() shows how the code reached the current line, even when nothing went wrong. I use it to track down who keeps calling a function:
import traceback
def audit():
traceback.print_stack(limit=3) # where are we? no exception needed
def place_order():
audit()
def checkout():
place_order()
checkout()
Output:
File "C:\pyguides\trace_print_stack.py", line 10, in checkout
place_order()
File "C:\pyguides\trace_print_stack.py", line 7, in place_order
audit()
File "C:\pyguides\trace_print_stack.py", line 4, in audit
traceback.print_stack(limit=3) # where are we? no exception needed

limit=3 keeps it to the three innermost frames, ending at the print_stack() call itself.Stack traces for chained exceptions
When you’re raising a new exception while handling another, use raise ... from err. The traceback then shows both, so the original cause isn’t lost:
import traceback
def read_config(path):
try:
open(path)
except FileNotFoundError as err:
raise RuntimeError("config is missing") from err
try:
read_config("settings_2026.ini")
except RuntimeError:
traceback.print_exc()
Output:
Traceback (most recent call last):
File "C:\pyguides\trace_chain.py", line 5, in read_config
open(path)
FileNotFoundError: [Errno 2] No such file or directory: 'settings_2026.ini'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\pyguides\trace_chain.py", line 10, in <module>
read_config("settings_2026.ini")
File "C:\pyguides\trace_chain.py", line 7, in read_config
raise RuntimeError("config is missing") from err
RuntimeError: config is missing

Which traceback function to use
| Function | What it does |
|---|---|
traceback.print_exc() | Print the current exception’s traceback to stderr |
traceback.format_exc() | Return it as a string |
traceback.format_exception(err) | Format a saved exception (list of strings) |
logging.exception(msg) | Log at ERROR level with the traceback |
traceback.print_stack() | Print the current call stack, no exception needed |
These pair well with stack traces:
- Exception handling in Python
- UnboundLocalError in Python
- ModuleNotFoundError after pip install
- Python with open() as file
Frequently asked questions
How do I print a stack trace in Python?
Call traceback.print_exc() inside an except block. It prints the full traceback without stopping the program.
How do I get the traceback as a string?
Use traceback.format_exc() inside the except block, or "".join(traceback.format_exception(err)) for a saved exception.
How do I log an exception with its traceback?
Use logger.exception("message") inside the except block, or pass exc_info=True to another logging call.
How do I print the stack without an exception?
Call traceback.print_stack(). It shows the current call chain.
Why does print(e) not show the traceback?
print(e) only shows the exception message. The traceback lives in e.__traceback__, which the traceback module formats.
Where is the traceback module documented?
See traceback in the Python docs.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn