Skip to content

Voice agent traces finish early and subsequent spans are not correctly nested #2470

@adammw

Description

@adammw

Describe the bug

Voice agents sample code ends trace early and creates multiple 'parent' spans (spans with parent_id=None).

Image Note in the screenshot above:
  • voice_agent_workflow is manually created wrapper trace to display the output on the same chart
  • Voice Agent should be the root trace, however the invoke_agent spans are NOT nested below it
  • Voice Agent trace ends after first interaction/turn, when it should last the entire conversation

Debug information

  • Agents SDK version: 0.8.3
  • Python version: 3.13.7

Repro steps

Use the Voice agents sample code, modified with:

from agents.tracing import TracingProcessor, set_trace_processors
import logging
logging.basicConfig(level=logging.DEBUG)

class DebugTracingProcessor(TracingProcessor):
    def __init__(self):
        super().__init__()
        self.logger = logging.getLogger(__name__)
        self.trace_stack = []  # Track trace hierarchy
        self.root_trace_id = None

    def _filter(self, span: dict[str, Any] | None) -> dict[str, Any] | None:
        if span is None:
            return {}
        # filter out span_data.input.data to avoid logging large audio data
        span_data = span.get("span_data", {})
        input_data = span_data.get("input")
        if isinstance(input_data, dict) and "data" in input_data:
            span["span_data"]["input"]["data"] = "[filtered]"
        output_data = span_data.get("output")
        if isinstance(output_data, dict) and "data" in output_data:
            span["span_data"]["output"]["data"] = "[filtered]"
        return span

    def on_trace_start(self, trace):
        trace_export = trace.export()
        trace_id = trace_export.get("id")

        # Track the first trace as root
        if self.root_trace_id is None:
            self.root_trace_id = trace_id
            self.logger.debug("ROOT Trace start: %s", trace_export)
        else:
            self.logger.debug("NESTED Trace start (should be span): %s", trace_export)

        self.trace_stack.append(trace_id)

    def on_trace_end(self, trace):
        trace_export = trace.export()
        trace_id = trace_export.get("id")

        if trace_id in self.trace_stack:
            self.trace_stack.remove(trace_id)

        # Only log trace end for the root trace
        if trace_id == self.root_trace_id and len(self.trace_stack) == 0:
            self.logger.debug("ROOT Trace end: %s", trace_export)
            self.root_trace_id = None
        else:
            self.logger.debug("NESTED Trace end (premature?): %s", trace_export)

    def on_span_start(self, span):
        span_export = self._filter(span.export())
        self.logger.debug("Span start: %s", span_export)

    def on_span_end(self, span):
        span_export = self._filter(span.export())
        self.logger.debug("Span end: %s", span_export)

    def shutdown(self):
        self.logger.debug("DebugTracingProcessor shutdown called")

    def force_flush(self):
        self.logger.debug("DebugTracingProcessor force_flush called")

set_trace_processors([DebugTracingProcessor()])

You should see logs after the first transcription request such as

DEBUG:__main__:ROOT Trace end: {'object': 'trace', 'id': 'trace_02c45c282ea34f4f86bf1702d37d3e2c', 'workflow_name': 'Voice Agent', 'group_id': 'group_554afae4eb054c84bb56cb51', 'metadata': None}
DEBUG:openai.agents:Resetting current trace
DEBUG:openai.agents:Creating span <agents.tracing.span_data.AgentSpanData object at 0x112e9e170> with id None
DEBUG:__main__:Span start: {'object': 'trace.span', 'id': 'span_fafab093c3a34f848d5ad7b7', 'trace_id': 'trace_02c45c282ea34f4f86bf1702d37d3e2c', 'parent_id': None, 'started_at': '2026-02-11T06:37:55.103214+00:00', 'ended_at': None, 'span_data': {'type': 'agent', 'name': 'Assistant', 'handoffs': ['Spanish'], 'tools': None, 'output_type': 'str'}, 'error': None}
DEBUG:openai.agents:Starting turn 1, current_agent=Assistant
DEBUG:openai.agents:No conversation_id available for request

Expected behavior

  • Root trace does not finish until end of conversation
  • Child spans all have the root trace as their parent

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions