Skip to content

[bug] Teams inside Workflows create separate root traces instead of nesting + Failed to detach context errors on every streamed response.  #2917

@yalishanda42

Description

@yalishanda42

Describe the bug
Two related issues when using AgnoInstrumentor with async streaming (arun with stream=True, stream_events=True):

  1. Failed to detach context errors on every streamed response. The arun_stream wrapper in _runs_wrapper.py:574 yields inside trace_api.use_span(span, end_on_exit=False). When the
    async generator is closed (e.g. the consumer finishes iterating), GeneratorExit is raised during yield, which triggers use_span's exit → context.detach(token). By this point
    the ContextVar token belongs to a different async context, so detach raises ValueError: <Token ...> was created in a different Context. This is a fundamental issue with OTEL
    context propagation across async generator boundaries in Python. Should be related to [bug] Agno #2698
  2. Teams inside Workflows create separate root traces instead of nesting. _get_team_span_context checks for _AGNO_PARENT_NODE_CONTEXT_KEY to decide whether a Team is top-level. If
    no parent Team is found, it returns trace_api.set_span_in_context(trace_api.INVALID_SPAN), forcing a new root trace. However, when a Team runs as a step inside a Workflow, the
    Workflow does not set _AGNO_PARENT_NODE_CONTEXT_KEY — it creates its own span via the workflow wrapper, not via the Team-specific context mechanism. As a result, the Team's
    internal arun always appears as a separate top-level trace, disconnected from the Workflow trace.

To Reproduce

from agno.agent import Agent
  from agno.models.openai import OpenAIChat
  from agno.team import Team
  from agno.workflow import Workflow
  from openinference.instrumentation.agno import AgnoInstrumentor
  from opentelemetry.sdk.trace import TracerProvider
  from opentelemetry.sdk.trace.export import SimpleSpanProcessor, ConsoleSpanExporter
  from opentelemetry import trace as trace_api

  tracer_provider = TracerProvider()
  tracer_provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
  trace_api.set_tracer_provider(tracer_provider)

  AgnoInstrumentor().instrument()

  agent_a = Agent(
      name="Agent A",
      model=OpenAIChat(id="gpt-4o-mini"),
      role="You answer questions briefly.",
  )

  team = Team(
      name="My Team",
      model=OpenAIChat(id="gpt-4o-mini"),
      members=[agent_a],
  )

  workflow = Workflow(
      name="My Workflow",
      steps=[team],
  )

  import asyncio

  async def main():
      stream = workflow.arun(input="Say hello", stream=True, stream_events=True)
      async for event in stream:
          pass  # consume the stream

  asyncio.run(main())

Observed behavior

  • Console shows ERROR - Failed to detach context with ValueError: <Token ...> was created in a different Context — triggered by GeneratorExit in arun_stream at
    _runs_wrapper.py:574.
  • The exported spans show two separate traces: one for the Workflow (My_Workflow.arun → My_Team.aexecute_stream) and a second disconnected root trace for the Team's internal run
    (My_Team.arun → Agent_A.arun → LLM call).

Expected behavior

  • No Failed to detach context errors during normal stream consumption.
  • All spans should be part of a single trace: My_Workflow.arun → My_Team.aexecute_stream → My_Team.arun → Agent_A.arun → LLM call.

Screenshots
(not actual screenshots because my use case is proprietary but those trees are based on the crafted example which I hand't run but guarantee that will work the same way)

Trace 1 (Workflow trace — Team's internal run is missing):
Workflow.arun
└── Team.aexecute_stream
└── Agent.aexecute_stream
└── Agent.arun
└── OpenAIChat.ainvoke_stream

Trace 2 (Team's internal run — disconnected from Workflow):
Team.arun
└── Team.arun
└── OpenAIChat.ainvoke_stream

Desktop (please complete the following information):

  • OS: macOS Darwin 25.3.0
  • Python: 3.12.11
  • agno: 2.5.3
  • openinference-instrumentation-agno: 0.1.28
  • opentelemetry-api / opentelemetry-sdk: 1.40.0

Additional context
The context detach error is also reported in #2698 — same root cause, same stack trace.

Root cause analysis for issue 2 (separate traces):

In _runs_wrapper.py, _get_team_span_context returns INVALID_SPAN context for any Team without a parent Team context:

  def _get_team_span_context(agent_or_team):
      if not isinstance(agent_or_team, Team):
          return None
      parent_team_node_id = context_api.get_value(_AGNO_PARENT_NODE_CONTEXT_KEY)
      if parent_team_node_id is None:
          return trace_api.set_span_in_context(trace_api.INVALID_SPAN)  # forces new root
      return None

This doesn't account for Teams running inside Workflows. The Workflow wrapper creates an active span, but doesn't set _AGNO_PARENT_NODE_CONTEXT_KEY. So the Team always sees None
and forces a new root trace.

Suggested fix: Also check for an active recording span before forcing a root:

  def _get_team_span_context(agent_or_team):
      if not isinstance(agent_or_team, Team):
          return None

      # If there's already an active recording span (e.g. from a Workflow),
      # let the Team nest under it instead of forcing a new root trace
      current_span = trace_api.get_current_span()
      if current_span and current_span.is_recording():
          return None

      parent_team_node_id = context_api.get_value(_AGNO_PARENT_NODE_CONTEXT_KEY)
      if parent_team_node_id is None:
          return trace_api.set_span_in_context(trace_api.INVALID_SPAN)
      return None

I used claude code to generate this and hadn't thoroughly verified it but I hope it's correct or at least helpful.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinginstrumentationAdding instrumentations to open source packages

Type

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions