Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

[Bug]: QueueShutDown spans still end as ERROR (and exceptions are recorded twice) because start_as_current_span sets status on exception

クローズ
#1,308 コメント 0 件 リアクション 0 件 担当者 2 名 GitHub で見る

メンテナーはふだん 2 日以内に返信

@rohityan がすでに取り組んでいます。

2026年10月3日 から。

  • #1309 @ferponse による — オープン

評価

この issue はまだ評価されていません。

説明

What happened?

EventQueueSource.dequeue_event spans still end with status ERROR (AsyncQueueShutDown: ) on every normal queue teardown, even though #1075 (fix for #1065) added QueueShutDown to _NON_ERROR_EXCEPTIONS.

trace_function opens its span with OTel's defaults:

with tracer.start_as_current_span(actual_span_name, kind=kind) as span:

start_as_current_span defaults to record_exception=True and set_status_on_exception=True. So when the wrapper re-raises, OTel's use_span records the exception and calls span.set_status(ERROR, ...) for any Exception that leaves the block (opentelemetry/trace/__init__.py, use_span).

  • QueueShutDown still ends as ERROR. The except _NON_ERROR_EXCEPTIONS arm correctly leaves the status alone and re-raises. OTel then marks the span ERROR anyway.
  • Every Exception is recorded twice. Both except arms call span.record_exception, and OTel records it a second time. So each such span carries two exception events.

CancelledError isn't affected, because it derives from BaseException and OTel only catches Exception.

The existing test (test_trace_function_async_non_error_exception_does_not_mark_span_error) uses a mocked tracer, so it only sees the wrapper's own set_status calls and not what OTel does on exit.

Expected: a span that ends with QueueShutDown is not ERROR, and a failing span has a single exception event.

Reproduction (a2a-sdk 1.2.1, opentelemetry-sdk 1.42.1, Python 3.12; same code on main at 6ff0a82):

import asyncio
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter

exporter = InMemorySpanExporter()
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(exporter))
trace.set_tracer_provider(provider)

from a2a.utils._async_queue_compat import QueueShutDown
from a2a.utils.telemetry import trace_function

@trace_function(span_name="shutdown")
async def shutdown():
    raise QueueShutDown()

@trace_function(span_name="boom")
async def boom():
    raise ValueError("real failure")

async def main():
    for f in (shutdown, boom):
        try:
            await f()
        except Exception:
            pass

asyncio.run(main())
for s in exporter.get_finished_spans():
    print(s.name, s.status.status_code.name, repr(s.status.description), [e.name for e in s.events])

Output:

shutdown ERROR 'AsyncQueueShutDown: ' ['exception', 'exception']
boom ERROR 'ValueError: real failure' ['exception', 'exception']

Both spans carry the exception twice, and boom's description is OTel's (ValueError: real failure), which overwrote the wrapper's str(e).

With the proposed fix, the same script prints:

shutdown UNSET None ['exception']
boom ERROR 'real failure' ['exception']

We noticed it on an A2A server running DefaultRequestHandlerV2: every streaming request leaves one ERROR dequeue_event span, which shows up as noise in the error rate of the trace backend.

Proposed fix: pass record_exception=False, set_status_on_exception=False to start_as_current_span in both wrappers, since the wrapper already does both itself. PR to follow.

Relevant log output
"name": "a2a.server.events.event_queue_v2.EventQueueSource.dequeue_event",
"kind": "SpanKind.SERVER",
"status": {
    "status_code": "ERROR",
    "description": "QueueShutDown: "
},
"events": [
    {
        "name": "exception",
        "attributes": {
            "exception.type": "culsans.QueueShutDown",
Code of Conduct
  • I agree to follow this project's Code of Conduct
主要言語
Python
スター
2.2k
フォーク
509
平均マージ
3日 11時間
マージ済み PR(30日)
43

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

a2aproject/a2a-python のほかの issue

a2aproject/a2a-python の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。