From e27f6360341953f97e35827dd5f757575872062d Mon Sep 17 00:00:00 2001 From: euisuh Date: Fri, 24 Jul 2026 11:28:36 +0900 Subject: [PATCH 1/2] fix(cohere): avoid double-ending async error spans --- .../instrumentation/cohere/__init__.py | 1 - .../tests/test_async_error_handling.py | 35 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 packages/opentelemetry-instrumentation-cohere/tests/test_async_error_handling.py diff --git a/packages/opentelemetry-instrumentation-cohere/opentelemetry/instrumentation/cohere/__init__.py b/packages/opentelemetry-instrumentation-cohere/opentelemetry/instrumentation/cohere/__init__.py index 5430ec9414..beffc4dd02 100644 --- a/packages/opentelemetry-instrumentation-cohere/opentelemetry/instrumentation/cohere/__init__.py +++ b/packages/opentelemetry-instrumentation-cohere/opentelemetry/instrumentation/cohere/__init__.py @@ -287,7 +287,6 @@ async def _awrap( if span.is_recording(): span.set_status(Status(StatusCode.ERROR, str(e))) span.record_exception(e) - span.end() raise set_span_response_attributes(span, response) diff --git a/packages/opentelemetry-instrumentation-cohere/tests/test_async_error_handling.py b/packages/opentelemetry-instrumentation-cohere/tests/test_async_error_handling.py new file mode 100644 index 0000000000..4dce501378 --- /dev/null +++ b/packages/opentelemetry-instrumentation-cohere/tests/test_async_error_handling.py @@ -0,0 +1,35 @@ +import logging + +import cohere +import pytest +from opentelemetry.instrumentation.cohere import CohereInstrumentor +from opentelemetry.trace import StatusCode + + +@pytest.mark.asyncio +async def test_async_cohere_exception_records_error_once( + caplog, monkeypatch, span_exporter, tracer_provider +): + async def failing_chat(self, *args, **kwargs): + raise RuntimeError("cohere async failure") + + monkeypatch.setattr(cohere.AsyncClient, "chat", failing_chat) + instrumentor = CohereInstrumentor() + instrumentor.instrument(tracer_provider=tracer_provider) + + try: + with caplog.at_level(logging.WARNING, logger="opentelemetry.sdk.trace"): + with pytest.raises(RuntimeError, match="cohere async failure"): + await cohere.AsyncClient("test_api_key").chat( + model="command", message="Tell me a joke, pirate style" + ) + finally: + instrumentor.uninstrument() + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + span = spans[0] + assert span.name == "cohere.chat" + assert span.status.status_code == StatusCode.ERROR + assert "cohere async failure" in span.status.description + assert not any("Calling end() on an ended span" in record.message for record in caplog.records) From 9c303fd199450a1a12ad6f2aedec930029170478 Mon Sep 17 00:00:00 2001 From: euisuh Date: Fri, 24 Jul 2026 13:29:00 +0900 Subject: [PATCH 2/2] test(cohere): assert async exception event Extend the async error-path regression test to assert the recorded exception event message, matching CodeRabbit review feedback. --- .../tests/test_async_error_handling.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/opentelemetry-instrumentation-cohere/tests/test_async_error_handling.py b/packages/opentelemetry-instrumentation-cohere/tests/test_async_error_handling.py index 4dce501378..73484ca2a6 100644 --- a/packages/opentelemetry-instrumentation-cohere/tests/test_async_error_handling.py +++ b/packages/opentelemetry-instrumentation-cohere/tests/test_async_error_handling.py @@ -32,4 +32,11 @@ async def failing_chat(self, *args, **kwargs): assert span.name == "cohere.chat" assert span.status.status_code == StatusCode.ERROR assert "cohere async failure" in span.status.description - assert not any("Calling end() on an ended span" in record.message for record in caplog.records) + assert any( + event.name == "exception" + and "cohere async failure" in event.attributes.get("exception.message", "") + for event in span.events + ) + assert not any( + "Calling end() on an ended span" in record.message for record in caplog.records + )