From 6ea40bba665f5ed5359562cf8a430f18163794d5 Mon Sep 17 00:00:00 2001 From: RRocaP Date: Sat, 11 Apr 2026 15:21:31 +0200 Subject: [PATCH] fix(langchain): detach orphaned context tokens in _create_llm_span When _create_llm_span() is called for a run_id that already has a SpanHolder, the existing entry is silently overwritten. The old holder's context token (and association_properties_token) are lost without being detached, which corrupts the OpenTelemetry context stack. This adds a check before the overwrite: if an existing SpanHolder is found, its tokens are safely detached before being replaced. Follows the same pattern used in _end_span() and aligns with the fixes in #3526 and #3807. Fixes #3957 --- .../instrumentation/langchain/callback_handler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/callback_handler.py b/packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/callback_handler.py index 28315b7f10..dc12735498 100644 --- a/packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/callback_handler.py +++ b/packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/callback_handler.py @@ -472,6 +472,14 @@ def _create_llm_span( # If context setting fails, continue without suppression token token = None + # Detach orphaned context tokens before overwriting (see #3957). + existing = self.spans.get(run_id) + if existing is not None: + if existing.token: + self._safe_detach_context(existing.token) + if getattr(existing, "association_properties_token", None): + self._safe_detach_context(existing.association_properties_token) + self.spans[run_id] = SpanHolder( span, token, None, [], workflow_name, None, entity_path )