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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions