Skip to content

fix(client): route span-activation detach through safe helper - #1807

Open
codechrl wants to merge 3 commits into
langfuse:mainfrom
codechrl:fix-branch
Open

fix(client): route span-activation detach through safe helper#1807
codechrl wants to merge 3 commits into
langfuse:mainfrom
codechrl:fix-branch

Conversation

@codechrl

@codechrl codechrl commented Aug 11, 2026

Copy link
Copy Markdown

Bug

When start_as_current_observation(end_on_exit=False) is closed from a different execution context than the one it opened in (normal in async code where the manual close runs on another task), the span-activation context token is detached in a context it was not created in. _start_as_current_otel_span_with_processed_media relied on OTel's start_as_current_span, whose internal context.detach is unguarded, so OTel logs Failed to detach context (ValueError: Token was created in a different Context) on every such close. Langfuse only guarded its own baggage token.

Fixes langfuse/langfuse#13590.

Fix

Activate the span directly (start_span + context.attach) and detach the span-activation token through the existing _detach_context_token_safely helper, which was built for exactly this case and already guards the baggage token. The span is ended explicitly when end_on_exit is set. Exception handling matches OTel's start_as_current_span defaults (record_exception + error status), so error observations are unchanged.

Verification

New unit test tests/unit/test_context_detach.py opens an observation, closes it on a copied asyncio context, and asserts no detach error is logged. Fails on main, passes with this change. Existing tests/unit otel/propagation/app-root suites stay green (221 passed, 2 skipped).

Greptile Summary

The PR replaces OpenTelemetry’s unguarded current-span context manager with explicit span activation and safe token detachment, preserving exception recording and conditional span ending.

  • Creates spans with start_span and activates them through the OTel context API.
  • Routes baggage and span tokens through the existing safe-detach helper.
  • Adds an async cross-context regression test for detach-error logging.

Confidence Score: 4/5

The implementation appears safe to merge, with a non-blocking test-isolation issue that should be addressed to prevent ambient span leakage between tests.

The production lifecycle change preserves the prior span behavior while safely handling cross-context detachment; only the regression test leaves stale ambient context after it returns.

Files Needing Attention: tests/unit/test_context_detach.py

Sequence Diagram

sequenceDiagram
    participant Caller
    participant Langfuse
    participant Context as OTel Context
    participant Span as OTel Span
    Caller->>Langfuse: Enter current observation
    Langfuse->>Span: start_span()
    Langfuse->>Context: attach(span context)
    Langfuse-->>Caller: observation wrapper
    Caller->>Langfuse: Exit from copied async context
    Langfuse->>Context: safely detach baggage token
    Langfuse->>Context: safely detach span token
    Langfuse->>Span: end() when end_on_exit
    Note over Context: Opening context remains unchanged when exit occurs in a copy
Loading
Prompt To Fix All With AI
### Issue 1
tests/unit/test_context_detach.py:32
**Test leaks the active span**

The observation is entered in the test's root context but exited only in a copied task context, so safe detachment cannot reset the token in the opening context. The test therefore returns with an ended span still current, making later context-sensitive tests order-dependent or assigning new observations the wrong parent.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(client): route span-activation detac..." | Re-trigger Greptile

Context used:

start_as_current_observation(end_on_exit=False) closed from a different
async context detached the OTel span-activation token in a context it was
not created in, so OTel logged 'Failed to detach context' (ValueError:
Token was created in a different Context) on every close. Manage span
activation directly and detach through the existing
_detach_context_token_safely helper (already used for the baggage token),
ending the span explicitly and preserving exception recording.

Fixes langfuse/langfuse#13590.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@CLAassistant

CLAassistant commented Aug 11, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: ValueError 'Token was created in a different Context' in start_as_current_observation() with OpenTelemetry contextvars

2 participants