What does traceback.format_exception() return when passed an exception object?
answer
- It takes the exception, not the current one
- The return value is not a string
- Join the pieces with the empty string
- One argument since a 3.1x release
- A default parameter controls the earlier exceptions
basics
~20 sIt returns a list of strings that concatenate to exactly the report the interpreter would print. Since Python 3.10 you pass just the exception instance; by default it also renders the exceptions reachable through cause and context.
solid answer
~40 s`traceback.format_exception(exc)` renders any exception you are holding — not only the one currently being handled — and returns a **list of `str`**, so `"".join(...)` gives the finished report. Since **3.10** the single-argument form is the signature; the legacy three-argument `(type, value, tb)` call still works, and the type argument is ignored and inferred from the value. `chain` defaults to `True`, so the output walks `__cause__` and `__context__` and prints the earlier exceptions above, joined by the standard banner lines; `chain=False` renders this exception alone. `limit` bounds the frames rendered. If you want only the final `TypeError: ...` line without any stack, `traceback.format_exception_only(exc)` gives that. `traceback.print_exception` is the same renderer writing to a stream instead.
code
python · 17 linesimport traceback
def load(record):
return record["timestamp"]
held = None
try:
try:
load({})
except KeyError as exc:
raise ValueError("record is missing a timestamp") from exc
except ValueError as exc:
held = exc
print("".join(traceback.format_exception(held)))
print(traceback.format_exception_only(held))
print(len(traceback.format_exception(held, chain=False)), "chunks without the chain")go deeper
Remember the practical shape: this one takes the exception object and gives you back a list of strings you join with the empty string. It is the function to reach for when the failure is not the one you are currently handling.
Explain the 3.10 single-argument signature, the list return value, and what chain=True adds to the output. Be able to say why it exists alongside format_exc rather than duplicating it.
Show judgement about report content: what you keep versus suppress, when the chain is signal, and how you pair a one-line summary from format_exception_only with the full rendered text for grouping failures.
Decide the house format for failure reports — what is stored, what is indexed, what is discarded — and make it uniform enough that failures from different services can be compared without a human reading every report.
### Why this function exists next to format_exc `traceback.format_exc()` can only render the exception currently being handled. `traceback.format_exception()` renders **an exception object you hand it**, which makes it the function you need once the failure has travelled: pulled off a queue, returned by `concurrent.futures.Future.exception()`, collected into a list during a batch run, or stashed on an object for a later report. ### The signature changed in 3.10 Historically the call was `format_exception(etype, value, tb)` — the three members of `sys.exc_info()`. Since **3.10** the exception instance alone is the intended positional argument, because an exception already carries its traceback on `__traceback__` and its class is its type. The old three-argument form is still accepted for compatibility, and in it the first argument is ignored and the type is inferred from the value. `format_exception_only` and `print_exception` gained the same single-argument form at the same time. If you are reading code that passes three arguments, it is either old or written by someone who learned the API before 3.10. ### The return value is a list, and that is deliberate You get a `list[str]`, not a string. Each element ends in a newline and some elements span several lines — the frame line plus its source line arrive together, for instance. Joining with the empty string, `"".join(format_exception(exc))`, produces exactly the text the interpreter would have written to `sys.stderr` for an unhandled exception. Joining with `"\n"` is a common bug: it doubles every line break. The list shape exists so a caller can stream the pieces to a writer without building the whole report in memory, and so tooling can post-process chunk by chunk. In application code you almost always join it immediately. ### Chaining in the output `chain=True` is the default. When set, the renderer follows the exception's `__cause__` first and its `__context__` otherwise, rendering the *earlier* exception above the later one and separating them with a fixed banner: an explicit `raise ... from ...` cause reads as a direct cause, while an exception raised incidentally inside a handler reads as having occurred during the handling of the previous one. `__suppress_context__`, which `raise ... from None` sets, tells the renderer to stop — that is how you deliberately hide an internal failure from the report. Passing `chain=False` renders only the exception you passed. That is occasionally what you want when the context chain is noise — a wrapper library that re-raises through three layers — but it is a lossy choice, and in a bug report the chain is usually the most valuable part of the text. Since **3.11**, an `ExceptionGroup` renders as an indented tree rather than a flat list, with each contained exception shown under its own branch. The same renderer handles it; you do not need a different call. ### format_exception_only `traceback.format_exception_only(exc)` returns just the type-and-message part — typically a single-element list like `["ValueError: bad record\n"]`, though a `SyntaxError` adds the offending source line and its marker. Use it for a one-line summary field alongside the full report: the summary is what you group failures by, the full text is what you read once you have picked a group. ### Choosing between the four renderers They are one machine with four front doors. `format_exc()` — current exception, one string. `format_exception(exc)` — any exception, list of strings. `print_exc()` and `print_exception(exc)` — the same two inputs, written to a stream, returning `None`. Everything else, `limit` and `chain`, is shared. Knowing which input each takes is the whole of the API, and getting it wrong is why people wrap `format_exc()` in a helper that mysteriously prints the wrong failure when called outside the handler. ### Summary line plus full report The pattern worth carrying away is to produce two things from one failure: the one-line summary from `format_exception_only()`, which is short enough to be a field you group and count on, and the joined output of `format_exception()`, which is the detail a human opens once. Storing only the full text forces anyone looking for patterns to parse it back; storing only the summary throws away the frames that tell you where to look. Both are cheap, and they answer different questions. ### What the renderer does not give you It gives you text and nothing else. There are no fields, no per-frame structure and no stable identity for the failure — two occurrences differing by one line number are two different strings. When you need frames as data rather than as a report, the traceback module has a separate extraction path for that, and mixing the two up — parsing the rendered text with a regular expression to recover file names — is the mistake this API exists to prevent.
- Why does joining the result with a newline produce doubled blank lines?Every element of the returned list already ends in a newline, and some elements carry several lines of their own. Joining with the empty string reproduces the interpreter's exact output; joining with `"\n"` inserts a second break after each chunk, which is why the report comes out double-spaced.
- When would you pass chain=False to traceback.format_exception?When the earlier exceptions are noise rather than signal — for example a wrapper that re-raises through several layers and whose intermediate failures add nothing. It is a lossy choice: the chain usually carries the original cause, so most reports are better off with the default.
- What is traceback.format_exception_only useful for?It returns just the type-and-message portion with no frames, so it makes a good short summary field: group or deduplicate failures by that one line, and keep the full rendered report as the detail you open afterwards. For a SyntaxError it additionally includes the offending source line and its position marker.
saying these in an interview costs you the question
- Says it returns a single ready-to-log string
- Joins the returned list with a newline
- Insists the three-argument call is still required
- Thinks it only works on the current exception
- Believes chained exceptions need a separate call