You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
serialize Langfuse client initialization so concurrent first calls construct only one client
close root trace contexts and flush active traces on normal finish and process exit
support fixed trace IDs only in explicit EvalOps context, preventing accidental trace-id reuse if the env var leaks
add tests for client init concurrency, fixed trace-id guard, root context close, and active-trace shutdown
Why
The Langfuse plugin can lose child observations when a process exits before active traces are finalized. Concurrent first-use initialization can also construct multiple clients and leak the losing client's background resources. These changes keep tracing fail-open while making trace finalization and client lifecycle more deterministic.
TOCTOU fix — the double-checked locking pattern (_LANGFUSE_CLIENT_LOCK separate from _STATE_LOCK) correctly prevents two concurrent first-callers from both constructing Langfuse(**kwargs) and leaking the loser's HTTP connection + flush thread. The fast path (_LANGFUSE_CLIENT is not None) avoids lock contention on the hot path.
Root context lifecycle — _close_root_context() extracts and nulls state.root_ctx, calls __exit__, and is idempotent. Called from both _finish_trace (normal path) and _shutdown_active_traces (atexit path). Previously the root context was never explicitly closed — the propagate_attributes context manager's __exit__ was relied upon implicitly.
atexit handler — _shutdown_active_traces() iterates all active trace states, ends pending observations, closes root contexts, and flushes. The _TRACE_STATE.clear() inside _STATE_LOCK ensures no double-processing if called concurrently with _finish_trace.
Fixed trace ID — correctly gated behind EvalOps context (HERMES_EVALOPS_RUN_ID or HERMES_EVALOPS_CASE_ID); standalone HERMES_LANGFUSE_TRACE_ID without EvalOps metadata triggers a warning and is ignored.
Test coverage — concurrency test uses threading.Barrier(8) to force race conditions, verifying exactly 1 Langfuse instance is created. Trace lifecycle tests cover _close_root_context idempotency and _shutdown_active_traces double-call safety.
No issues found. The _close_root_context fix addresses a real resource leak where propagate_attributes.__exit__ was never called on abnormal trace termination.
Thanks for addressing a real lifecycle gap. On current main, _start_root_trace manually enters and retains root_ctx (plugins/observability/langfuse/__init__.py:624-667), while _finish_trace only ends root_span (:737-766), so explicit context closure is useful.
Problems
Current main also evicts non-finalizing traces in _evict_stale_locked (plugins/observability/langfuse/__init__.py:713-735). That path ends only root_span; the proposed helper is not applied there. Salvage should close the evicted root_ctx too and add a regression test.
The diff adds HERMES_LANGFUSE_TRACE_ID and HERMES_EVALOPS_* as behavioral environment settings. AGENTS.md:102-107 requires new non-secret behavior configuration to use config.yaml, not .env.
Suggested changes
Apply root-context closure to the current eviction path and test it.
Split out or rework the fixed-trace-id/EvalOps configuration surface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
comp/pluginsPlugin system and bundled pluginsP3Low — cosmetic, nice to havesweeper:blast-containedSweeper blast radius: contained — one narrow path / opt-in / few userssweeper:risk-compatibilitySweeper risk: may break existing users, config, migrations, defaults, or upgradestype/bugSomething isn't working
4 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Why
The Langfuse plugin can lose child observations when a process exits before active traces are finalized. Concurrent first-use initialization can also construct multiple clients and leak the losing client's background resources. These changes keep tracing fail-open while making trace finalization and client lifecycle more deterministic.
Tests