Skip to content

SpanImpl.__exit__ logs GeneratorExit/CancelledError as span errors, causing false failures on every ADK sub-agent transfer #620

Description

@chen2739

SpanImpl.__exit__ (braintrust/logger.py:4919-4923) logs any non-None exc_type as a span error:

def __exit__(self, exc_type, exc_value, tb) -> None:
    try:
        if exc_type is not None:
            self.log_internal(dict(error=stringify_exception(exc_type, exc_value, tb)))
    ...

This doesn't distinguish real exceptions from BaseException-only control-flow signals like GeneratorExit and asyncio.CancelledError, which represent normal cleanup, not failures.

This fires reliably when tracing google-adk (2.4.0) agents with sub_agents. ADK's node scheduler (google/adk/workflow/_llm_agent_wrapper.py:381-409) wraps every agent run in async with aclosing(run_method) as run_iter: and does:

if event.actions.transfer_to_agent:
    ...
    transferred = True
    break   # closes run_iter via aclosing.__aexit__ -> throws GeneratorExit

The GeneratorExit propagates through the integration's own aclosing wrappers in braintrust/integrations/adk/tracing.py (_agent_run_async_wrapper, _flow_run_async_wrapper, _flow_call_llm_async_wrapper), each suspended at yield event inside a with start_span(...) block. That with block's __exit__ is SpanImpl.__exit__, which then logs the GeneratorExit as an error.

Impact: any orchestrator agent with sub_agents gets a false error logged on essentially every delegation step, since transfer_to_agent triggers this on every hand-off. A single-agent setup with no sub_agents never hits the transfer_to_agent branch, so the bug is easy to miss until you have a multi-agent workflow.

Suggested fix: in SpanImpl.__exit__, only treat it as an error when exc_type is a real Exception (or explicitly exclude GeneratorExit / asyncio.CancelledError):

if exc_type is not None and issubclass(exc_type, Exception):
    self.log_internal(dict(error=stringify_exception(exc_type, exc_value, tb)))

Repro: an ADK LlmAgent with one or more sub_agents, instrumented via braintrust.wrappers.adk.setup_adk, run through InMemoryRunner. Every transfer_to_agent delegation logs a bogus error on the corresponding span.

Versions: braintrust==0.30.1, google-adk==2.4.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions