# PEP 830: Add timestamps to exceptions and tracebacks

**URL:** <https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942>\
**Category:** PEPs\
**Tags:** pep\
**Created:** [April 12, 2026, 8:36pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942 "2026-04-12T20:36:42Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 12, 2026, 8:36pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/1 "2026-04-12T20:36:43Z")

</div>

This PEP adds an optional timestamp to all exceptions that records when the exception was instantiated with no observable overhead. When enabled via environment variable or command-line flag, formatted tracebacks display this timestamp alongside the exception message.

If you’ve ever debugged a production Python system involving many forms of concurrency and wished you had more information about what happened when, this PEP may be for you. The point at which an exception is logged or rendered with a logging system supplied timestamp is often not the same as the time it happened.

If you never debug complex applications like that, you won’t even notice that this feature exists. It is off by default, to be enabled only by those in environments who feel the above pain. Win win.

> **[PEP 830 – Add timestamps to exceptions and tracebacks | peps.python.org](https://peps.python.org/pep-0830/)**
>
> This PEP adds an optional \_\_timestamp\_ns\_\_ attribute to BaseException that records when the exception was instantiated with no observable overhead. When enabled via environment variable or command-line flag, formatted tracebacks display this...

---

<div class="post-metadata">

**Author:** ![iritkatriel](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/iritkatriel/32/2615_2.png) [@iritkatriel](https://discuss.python.org/u/iritkatriel)\
**Post date:** [April 12, 2026, 9:01pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/2 "2026-04-12T21:01:31Z")

</div>

I think the time it was raised may be more interesting than the time it was instantiated.

---

<div class="post-metadata">

**Author:** ![bwoodsend](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/bwoodsend/32/4027_2.png) [@bwoodsend](https://discuss.python.org/u/bwoodsend)\
**Post date:** [April 12, 2026, 10:08pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/3 "2026-04-12T22:08:47Z")

</div>

Can this not already be done with something like:

```python
def excepthook(exctype, value, traceback):
    value.add_note(f"Raised at {datetime.now()}")
    sys. __excepthook__ (exctype, value, traceback)

sys.excepthook = excepthook

```

(P.S. It would help if the example in the PEP was self contained so I could try it.)

---

<div class="post-metadata">

**Author:** ![ncoghlan](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ncoghlan/32/14266_2.png) [@ncoghlan](https://discuss.python.org/u/ncoghlan)\
**Post date:** [April 12, 2026, 10:10pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/4 "2026-04-12T22:10:38Z")

</div>

> [@iritkatriel](#):
>
> think the time it was raised may be more interesting than the time it was instantiated.

That would be a more intrusive change, though, as the interpreter loop would need to store the info somewhere else until the exception was instantiated when a stateless exception is raised by type.

User code can also force the two times to match by instantiating when raising (which is necessarily the case for stateful exceptions).

---

<div class="post-metadata">

**Author:** ![iritkatriel](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/iritkatriel/32/2615_2.png) [@iritkatriel](https://discuss.python.org/u/iritkatriel)\
**Post date:** [April 12, 2026, 10:46pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/5 "2026-04-12T22:46:51Z")

</div>

> [@ncoghlan](#):
>
> That would be a more intrusive change, though, as the interpreter loop would need to store the info somewhere else until the exception was instantiated when a stateless exception is raised by type.

I think it can attach the timestamp in `do_raise` (see [here](https://github.com/python/cpython/blob/480edc1aae0f54e34d217c72eb2962702c82a666/Python/ceval.h#L517)) just after the raised type is instantiated.

> [@ncoghlan](#):
>
> User code can also force the two times to match by instantiating when raising (which is necessarily the case for stateful exceptions).

I was thinking about cases where the exception is created before the raise. Maybe it’s returned from some function, cached, and raised somewhere else at a later time. It’s also possible to raise the same exception instance more than once. And of course we would need to defined whether the timestamp is overwritten in a reraise (a naked raise or an explicit `raise e’). Setting the timestamp at object creation time bypasses these question because the object is only created once. But I’m questioning why that is the most semantically meaningful timestamp to use here.

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 12, 2026, 11:57pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/6 "2026-04-12T23:57:08Z")

</div>

How often does code actually have a distinction between time of instantiation and time of raise in practice?

I understand it is _possible_ to do a bare `raise SomeExceptionType` without an immediately constructing `(”message”)` argument I believe causes this. But that one doesn’t matter, `do_raise` immediately calls it to do the instantiation. Nevermind that bare raises without `()` don’t feel like a normal idiom I’ve seen in code since basically forever. Even in the C API, the `PyErr_Set*` APIs do the same thing from what I can tell by calling `_PyErr_CreateException`. It seems like most everything normalizes the exception into an instance pretty quickly.

Does anyone have practical in-use examples of code creating an exception instance well before the raise, or even the unsatisfiable pattern of (eek!) re-using a pre-created exception instance across many disconnected raises? I believe we do this internally for some last-ditch `MemoryError` situations, but that’s a special case that seems fine to ignore. It is certainly _possible_ for people to write code in Python or C that does it. But why would anyone?

PEP-830 update TODO: We should probably explicitly zero out any singleton exception instance timestamps we have in our own internals.

Regardless, while pondering that I’m going to go brush up on the `ceval.h::do_raise` path to better understand possibilities.

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 12, 2026, 11:59pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/7 "2026-04-12T23:59:36Z")

</div>

This is also one of those areas where trying to be perfect could be not worthwhile. I expect instantiation time will cover the bulk of use cases. If we find otherwise through experience we could change how the time collection is done.

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [April 13, 2026, 12:04am UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/8 "2026-04-13T00:04:50Z")

</div>

What are the consequences on internal-use exceptions like `StopIteration`? Querying the time of day takes a notable amount of time:

```python
rosuav@sikorsky:~$ python3 -m timeit -s 'from time import time' 'time()'
10000000 loops, best of 5: 24 nsec per loop
rosuav@sikorsky:~$ python3 -m timeit -s 'from time import time' 'for _ in (): pass'
50000000 loops, best of 5: 5.87 nsec per loop
rosuav@sikorsky:~$ python3 -m timeit -s 'def time(): pass' 'time()'
20000000 loops, best of 5: 11.7 nsec per loop

```

and I’d hope that this can be avoided in these situations.

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 13, 2026, 12:05am UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/9 "2026-04-13T00:05:03Z")

</div>

did you read the PEP? (ie: StopIteration and StopAsyncIteration needs are answered in there already, yes this was worth considering and is a special case!)

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 13, 2026, 12:10am UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/10 "2026-04-13T00:10:21Z")

</div>

Regarding ceval’s `do_raise` - that probably isn’t the touchpoint to do it if we wanted time of raise rather than instantiation as it is eval loop focused.

But I think `Python/errors.c::_PyErr_SetRaisedException` _might_ be. Assuming everything goes through that rather than diddling with `tstate->current_exception` directly (which feels like a hard assumption to make, though it appears true by my ripgrepping).

---

<div class="post-metadata">

**Author:** ![dr\_carlos](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/dr_carlos/32/30461_2.png) [@dr\_carlos](https://discuss.python.org/u/dr_carlos)\
**Post date:** [April 13, 2026, 12:26am UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/11 "2026-04-13T00:26:05Z")

</div>

> [@gpshead](#):
>
> Does anyone have practical in-use examples of code creating an exception instance well before the raise

I have written code which ‘asserts’ a bunch of things by instantiating a exceptions throughout a function, adding them to a list and then raising them as an `ExceptionGroup` at the end. This removes the extra step of raising the exception each time, catching it, only to add it to an exception group. I’m not sure if this is _best practice_ or anything, but it’s a pattern that works for me.

So if the timestamp is added at raise-time, I think this PEP would no longer cover such exceptions as they are never actually raised. I’m not sure if this is a good or a bad thing, just something to consider.

---

<div class="post-metadata">

**Author:** ![Rosuav](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/rosuav/32/3429_2.png) [@Rosuav](https://discuss.python.org/u/Rosuav)\
**Post date:** [April 13, 2026, 2:23am UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/12 "2026-04-13T02:23:33Z")

</div>

> [@gpshead](#):
>
> (ie: StopIteration and StopAsyncIteration needs are answered in there already, yes this was worth considering and is a special case!)

Doh. I did skim it, and I was thinking initially about some others like AttributeError, but then when I went to make the point about the time cost, I switched to StopIteration because it’s easier to demonstrate. So, yes, that particular one IS handled (yay!). What I should have asked is: will things like `hasattr` calls become slower because of internal time queries?

```python
rosuav@sikorsky:~$ python3 -m timeit -s 'from time import time' 'time()'
10000000 loops, best of 5: 23.8 nsec per loop
rosuav@sikorsky:~$ python3 -m timeit 'hasattr(None, "test")'
20000000 loops, best of 5: 14.4 nsec per loop

```

Same applies (I think??) to `dict.setdefault` and ` __missing__ `. And maybe I should have stuck with my original thought, since that would have shown me that the time cost isn’t nearly as distinct.

---

<div class="post-metadata">

**Author:** ![iritkatriel](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/iritkatriel/32/2615_2.png) [@iritkatriel](https://discuss.python.org/u/iritkatriel)\
**Post date:** [April 13, 2026, 7:09am UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/13 "2026-04-13T07:09:04Z")

</div>

> [@gpshead](#):
>
> I understand it is _possible_ to do a bare `raise SomeExceptionType` without an immediately constructing `(”message”)`

By ‘bare raise’ I meant a `raise` without args (reraise currently handled exception).

I’m not concerned about the case of raising a type, it gets instantiated almost immediately so I don’t see a difference.

---

<div class="post-metadata">

**Author:** ![AndersMunch](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/andersmunch/32/12823_2.png) [@AndersMunch](https://discuss.python.org/u/AndersMunch)\
**Post date:** [April 13, 2026, 2:40pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/14 "2026-04-13T14:40:04Z")

</div>

About this:

> A Python API to toggle timestamps at runtime is unnecessary complexity. Applications that want timestamps are expected to enable them in their environment; a runtime toggle would make it harder to reason about program state.

Could we please not add features to Python that are managed from outside of Python, and can only be managed from outside of Python, unless the nature of the feature necessitates it? I want to program in Python, not in shell script.

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 13, 2026, 4:02pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/15 "2026-04-13T16:02:42Z")

</div>

> [@iritkatriel](#):
>
> By ‘bare raise’ I meant a `raise` without args (reraise currently handled exception).

The currently handled exception already has a timestamp and the original place it was raised from is the correct place for the time to have been recorded.

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 13, 2026, 4:11pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/16 "2026-04-13T16:11:00Z")

</div>

> [@iritkatriel](#):
>
> Maybe it’s returned from some function, cached, and raised somewhere else at a later time. It’s also possible to raise the same exception instance more than once. And of course we would need to defined whether the timestamp is overwritten in a reraise (a naked raise or an explicit `raise e’). Setting the timestamp at object creation time bypasses these question because the object is only created once. But I’m questioning why that is the most semantically meaningful timestamp to use here.

Okay so I gather this is what you were talking about in regards to the bare `raise` that reraises the currently being handled exception as one example. I do not think it is solvable to store a timestamp at every point a raise happens (complex and generally not necessary). It either needs to be time of instantiation, or time of first raise. Time of most recent raise could be far removed from the appropriate spot even in perfectly linear chains of exception handlers doing a reraise _after_ running additional logic. It’s an interesting thought to record each of the re-raises along the way, but no longer trivial.

---

<div class="post-metadata">

**Author:** ![vstinner](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/vstinner/32/15130_2.png) [@vstinner](https://discuss.python.org/u/vstinner)\
**Post date:** [April 13, 2026, 4:31pm UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/17 "2026-04-13T16:31:06Z")

</div>

I think that this idea (adding a timestamp to exceptions) was already discussed previously, but I failed to find the link. Or maybe it was your PR [gh-129337](https://github.com/python/cpython/pull/129337) that you created in January 2025.

I found the recent project [dttb](https://github.com/rocky-d/dttb) (created last January) which uses `sys.excepthook` to add a timestamp to exceptions. It uses `datetime.datetime.now()` and so has a resolution of 1 microsecond. It formats the timestamp at the beginning:

```python
[2026-01-28 14:30:15.123456+08:00]
Traceback (most recent call last):
  ...
Error: something wrong

```

I find it interesting to put the timestamp at the start and don’t change `str(exc)`.

Specification:

> A new read/write attribute ` __timestamp_ns__ ` is added to `BaseException`. It stores nanoseconds since the Unix epoch

You should mention that’s a timestamp in the UTC time zone.

> When timestamps are disabled, or for control flow exceptions (see below), the value is `0`.

You should also use the value `0` when reading the clock fails. It’s rare but it can happen when the syscall is blocked by a sandbox ([by mistake](https://bugzilla.redhat.com/show_bug.cgi?id=1887445)).

For the Python API (`exc. __timestamp_ns__ ` attribute), would it make sense to return `None` instead of `0` when the timestamp is not set?

> `PYTHON_TRACEBACK_TIMESTAMPS` environment variable  
> Set to `us` or `1` for microsecond-precision decimal timestamps, `ns` for nanoseconds, or `iso` for ISO 8601 UTC format. Empty, unset, or `0` disables timestamps (the default).

Can you mention that ISO 8601 format has a resolution of 1 microsecond?

Display Format:

> The `us` format produces `<@1776017164.530916>` and the `ns` format produces `<@1776017178687320256ns>`.

I would prefer to use `<@1776017178.687320256>` format for `ns`, similar to the `us` format but with 3 extra digits after the dot. For example, using this format you can copy/paste the number to `datetime.datetime.fromtimestamp()` and `time.gmtime()`:

```py
>>> import datetime as dt
>>> print(dt.datetime.fromtimestamp(1776017178.687320256))
2026-04-12 20:06:18.687320

>>> import time
>>> time.gmtime(1776017178.687320256)
time.struct_time(tm_year=2026, tm_mon=4, tm_mday=12, tm_hour=18, tm_min=6, tm_sec=18, tm_wday=6, tm_yday=102, tm_isdst=0)

```

Performance Measurements.

FYI Linux and other operating systems provide a `CLOCK_REALTIME_COARSE` clock faster than `CLOCK_REALTIME` but with a worse resolution. For example, on Linux 6.19.10 with glibc 2.42, I measured in Python that `CLOCK_REALTIME` has a resolution of 137 nanoseconds, whereas `CLOCK_REALTIME_COARSE` only has a resolution of 1 ms! I don’t think that 1 ms resolution is enough for exception timestamps. Reading `CLOCK_REALTIME_COARSE` takes `28.8 ns +- 0.7 ns`, whereas reading `CLOCK_REALTIME` takes `40.7 ns +- 1.6 ns`.

> `TracebackException` and the public formatting functions (`print_exc`, `print_exception`, `format_exception`, `format_exception_only`) gain a `no_timestamp` keyword argument (default `False`) that suppresses timestamp display even when globally enabled.

I dislike double negation (ex: `no_timestamp=False`). Would it be possible to change the parameter to `timestamp` instead (ex: `timestamp=True`)?

> Rejected Ideas: Millisecond Precision.

Why not also offering the choice to format a timestamp with microsecond resolution? (`PYTHON_TRACEBACK_TIMESTAMPS=ms`)

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 14, 2026, 12:17am UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/18 "2026-04-14T00:17:35Z")

</div>

The draft PR has been around a while as i’ve been noodling on doing it since early 2025. I presented the idea to the room in our Cambridge Core Team Sprint last September. The feedback there led me to tweaking the behavior a bit (to not collect the times at all when the feature is disabled rather than just controlling their display), better documenting the what and why, and turning it into a PEP.

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 14, 2026, 1:09am UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/19 "2026-04-14T01:09:03Z")

</div>

> [@vstinner](#):
>
> I found the recent project [dttb](https://github.com/rocky-d/dttb) (created last January) which uses `sys.excepthook` to add a timestamp to exceptions. It uses `datetime.datetime.now()` and so has a resolution of 1 microsecond.

Using the [`sys.excepthook` singleton](https://docs.python.org/3/library/sys.html#sys.excepthook) does not work for this purpose. It does not get called at the time of raise or construction, which would defeat the purpose for this use case of having every exception raised in an ExceptionGroup carry a timestamp at the point it occurred. It is also global state that other things can (and do) clobber for their own purposes.

FWIW, that `dttb` library was advertised in a drive by spammy comment [on my github tracking issue](https://github.com/python/cpython/issues/132502) for this feature a month ago. (_unclear if that was by the author or by their AI bot_) Regardless it is not an equivalent.

> [@vstinner](#):
>
> > Rejected Ideas: Millisecond Precision.
> 
> Why not also offering the choice to format a timestamp with microsecond resolution? (`PYTHON_TRACEBACK_TIMESTAMPS=ms`)

A feeling that it already offers plenty of choices? Supporting this would also be easy and suggests your idea to format the `ns` value as a decimal has merit as `ms` `us` and `ns` would all be formatted the same that way. Easy to add, but I don’t think it’d be necessary. If anything I could see dropping `us` support and only offering `ns` and `iso` modes (though probably using your decimal format for `ns`).

For the same reason, I do not want to support custom timestamp formatting strings other than `iso`. It just adds complexity for no meaningful value.

> [@vstinner](#):
>
> > A new read/write attribute ` __timestamp_ns__ ` is added to `BaseException`. It stores nanoseconds since the Unix epoch
> 
> You should mention that’s a timestamp in the UTC time zone.

I thought that was implied by being seconds since the unix epoch like everything in POSIX time land. I’ll add the note to make it clear in my next PEP update.

---

<div class="post-metadata">

**Author:** ![gpshead](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gpshead/32/54_2.png) [@gpshead](https://discuss.python.org/u/gpshead)\
**Post date:** [April 14, 2026, 1:29am UTC](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942/20 "2026-04-14T01:29:31Z")

</div>

re: Timer resolution - we always collect a nanosecond resolution value (which most OSes provide) regardless of the display format using `PyTime_TimeRaw`. Thus the attr being named ` __timestamp_ns__ `  
See [cpython/Objects/exceptions.c at 2672099c67cbeeb03c0dcba55e7975c999887d74 · python/cpython · GitHub](https://github.com/python/cpython/blob/2672099c67cbeeb03c0dcba55e7975c999887d74/Objects/exceptions.c#L97)

> [@vstinner](#):
>
> I dislike double negation (ex: `no_timestamp=False`). Would it be possible to change the parameter to `timestamp` instead (ex: `timestamp=True`)?

So do I. Now I need to remember why I did that… I _think_ it came from the only reason to ever use the new kwarg at all being to _disable_ the display. But that’s still better as `timestamp=False`. BUT a `timestamp=True` default or someone oddly explicitly passing that could be incorrectly interpreted as always displaying one even if collection is not enabled (not possible or it’d be 0) and no need for this to be a tri-state. Which could lead to wordier but accurate API name such as `allow_timestamps=False` with a default of `True`. A bit of a bikeshed.

[Next page](https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942.md?page=2)
