From c65a90448c57e36771a783c4f1c23cc69f5d9053 Mon Sep 17 00:00:00 2001 From: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> Date: Mon, 17 Aug 2026 22:19:48 +0100 Subject: [PATCH 1/2] fix(tracing): make trace finish idempotent --- src/agents/tracing/traces.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/agents/tracing/traces.py b/src/agents/tracing/traces.py index a703acf317..eda6d2f880 100644 --- a/src/agents/tracing/traces.py +++ b/src/agents/tracing/traces.py @@ -497,6 +497,7 @@ class TraceImpl(Trace): "_prev_context_token", "_processor", "_started", + "_finished", ) def __init__( @@ -516,6 +517,7 @@ def __init__( self._prev_context_token: contextvars.Token[Trace | None] | None = None self._processor = processor self._started = False + self._finished = False @property def trace_id(self) -> str: @@ -544,7 +546,9 @@ def finish(self, reset_current: bool = False): if not self._started: return - self._processor.on_trace_end(self) + if not self._finished: + self._processor.on_trace_end(self) + self._finished = True if reset_current and self._prev_context_token is not None: Scope.reset_current_trace(self._prev_context_token) From e64b6921c11eeef6315d1ab683edb9da47e5e18e Mon Sep 17 00:00:00 2001 From: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> Date: Mon, 17 Aug 2026 22:19:55 +0100 Subject: [PATCH 2/2] test(tracing): cover repeated trace finish --- tests/test_trace_finish_idempotent.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 tests/test_trace_finish_idempotent.py diff --git a/tests/test_trace_finish_idempotent.py b/tests/test_trace_finish_idempotent.py new file mode 100644 index 0000000000..6f5dc47f90 --- /dev/null +++ b/tests/test_trace_finish_idempotent.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from unittest.mock import MagicMock + +from agents.tracing.scope import Scope +from agents.tracing.traces import TraceImpl + + +def test_trace_finish_notifies_processor_once_and_can_reset_later() -> None: + processor = MagicMock() + trace = TraceImpl( + name="workflow", + trace_id="trace_test", + group_id=None, + metadata=None, + processor=processor, + ) + + trace.start(mark_as_current=True) + trace.finish() + trace.finish(reset_current=True) + + processor.on_trace_end.assert_called_once_with(trace) + assert Scope.get_current_trace() is None