PEP 830: Add timestamps to exceptions and tracebacks

Recording “time of first raise” timestamps as a “Deferred, but not rejected” idea sounds good to me. It might be possible, but it’s not obviously easy, and it’s far from clear that it’s necessary, so including it in the initial iteration carries a high risk of introducing significant complexity for negligible benefit.

4 Likes

We can do this with monitoring:

import sys

from datetime import datetime



# Define a tool ID

TOOL_ID = sys.monitoring.DEBUGGER_ID

sys.monitoring.use_tool_id(TOOL_ID, "ExceptionEnricher")



def on_exception(code, instruction_offset, exc):

    timestamp = datetime.now().isoformat()

    exc.add_note(f"Intercepted at: {timestamp}")



# Register the callback for raised exceptions

sys.monitoring.register_callback(TOOL_ID, sys.monitoring.events.RAISE, on_exception)

sys.monitoring.set_events(TOOL_ID, sys.monitoring.events.RAISE)





try:

    1/0

except:

    raise ValueError

Output:

Traceback (most recent call last):

  File "/Users/iritkatriel/src/tt.py", line 19, in <module>

    1**/**0

    \~**^**\~

**ZeroDivisionError**: division by zero

Intercepted at: 2026-04-15T22:31:49.170785

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

  File "/Users/iritkatriel/src/tt.py", line 21, in <module>

    raise ValueError

ValueError

Intercepted at: 2026-04-15T22:31:49.170810
9 Likes

Would it be possible/make sense to format the timestamp like that instead of changing str(exc)? It may avoid the need for traceback.strip_exc_timestamps() in some cases.

I think it would be better to add the timestamp output to the traceback’s first line, rather than altering the exception’s string, which is often used and parsed by real life applications and logging tools in lots of ways which would break:

Traceback (most recent call last, raised at 2026-04-12T18:07:30.346914Z):
  File "<stdin>", line 3, in california_raisin
    raise RuntimeError("not enough sunshine")
RuntimeError: not enough sunshine

The output also looks more readable that way.

14 Likes

Yes sys.monitoring could be used to do this today. But that is a heavyweight call and very heavyweight note. It’d impact application performance (including the pathological StopIteration and StopAsyncIteration cases).

I’ll make sure to cover this in the next update.

I think the ‘heavyweight’ claim requires benchmarking.

Note that there is a C API for monitoring.

Round 2: I’ve posted PR peps#4928 with edits incorporating feedback so far. The implementation has also been updated. short summary:

I dropped the us format in favor of just ns (now using decimal seconds) and iso options. (trivial to add ms and us as well but I don’t see a point in the complexity)

no_timestamp= was renamed to timestamps= in the traceback module APIs and I did wind up going with the tri-state as I came up with a reason why someone might want it.

Various clarifications that came up here that needed recording as more detail or recording either as rejected ideas or open issues (and sometimes both… rejected, but open to exploring, deferring? etc)

sys.monitoring - I dug into that a bit more and describe how it could be used and what it would require… which led to me deciding that was a rejected ideas piece more so than open issue: The RAISE event fires too often, the call overhead on all of those events is high, and it’d involve additional memory allocations and potentially collectable objects created upon every raise.

Lets please keep non-docs editorial discussion here rather than as PR comments. Far more details in the PEP update itself. Please read it. I’ll reply here once it has merged and published. For now, see the PR.

3 Likes

PR merged. It looks published. The PR is the easiest way to see the diff.

The SC has discussed and while we are generally positive about PEP 830 - Add timestamps to exceptions and tracebacks, we have decided to defer making a decision on this PEP until Python 3.16. We have feedback on collection and on the opt-in/display story that we’d like to see worked through before we make a decision.

On the collection:

  • The current performance numbers do not cover Windows or shared-library configurations (which may see significantly more overhead). We’d like to see numbers for those environments incorporated into the PEP so we can better evaluate the overhead claim across platforms.
  • Pickle output depends on the timestamp collection state, which we find awkward. We’d prefer an opt-out, either by not collecting, or by not pickling the timestamp attribute even when collection is enabled.

On opt-in and display:

  • The PEP appeals to CPython convention to justify silent-ignore on invalid env var values, but that convention is really for boolean toggles (e.g. PYTHONFAULTHANDLER, PYTHONDEVMODE) and not for enum-shaped flags (e.g. PYTHON_GIL, PYTHON_FROZEN_MODULES, PYTHONTRACEMALLOC, PYTHON_CPU_COUNT, PYTHONHASHSEED). Enum-shaped flags by and large fail-fatal on invalid values, and PYTHON_TRACEBACK_TIMESTAMPS should follow the convention of that peer group. This behavior should also be aligned across the env var and -X paths to avoid a confusing user experience and bifurcation of documentation.
  • PYTHON_TRACEBACK_TIMESTAMPS and -X traceback_timestamps= should accept one value per meaning. Each precision option should have a single canonical string (e.g. ns, us, ms, iso), with an explicit off for disabled. Equivalents like empty, 0, and 1 should be rejected as invalid rather than silently aliased to one of the canonical values.
  • The current proposed timestamp suffix appears on the message line in formatted traceback output and is per-occurrence, which means tooling that captures stderr or formatted tracebacks and matches on the message line sees a different string for every exception instance. The discussion thread raised header-line placement as an alternative that avoids this. We don’t think message-line placement is necessarily wrong, but the PEP should further justify the choice with a clear rationale and close out the open issue regarding display location. Further, placing the timestamp at the end of the line obscures it in ways that can be difficult to see.
  • Opt-out for doctest printing will likely break code and is backward incompatible. This is because doctests can be very sensitive to display output. We’d prefer an opt-in flag, e.g. doctest.INCLUDE_EXCEPTION_TIMESTAMPS which are not enabled by default.
  • Similarly test.support.force_no_traceback_timestamps should probably be enabled by default, possibly with a differently named flag to enable traceback timestamps in the stdlib test suite.

Thanks again for your work on this PEP, we look forward to discussing again with revisions.

8 Likes