Skip to content

fix(tracing): preserve inherited baggage tags when adding local tags - #1939

Draft
lumman0 wants to merge 2 commits into
langfuse:mainfrom
lumman0:fix/propagated-baggage-tags
Draft

lumman0 wants to merge 2 commits into
langfuse:mainfrom
lumman0:fix/propagated-baggage-tags

Conversation

@lumman0

@lumman0 lumman0 commented Oct 7, 2026

Copy link
Copy Markdown

What does this PR do?

Fixes langfuse/langfuse#18379.

Draft / dependency: #1919 must land first (or its agreed tag decoder must be available). This branch cherry-picks Ambuj Upadhyay's #1919 commit 22292d5fe13e530a1deb89214e9b3b95ada18d0a with original authorship and a cherry picked from trailer. The second commit is this PR's tag-merge change. I will update the branch against main once that dependency lands; this is not intended to replace #1919.

After W3C baggage crosses a process boundary, adding local tags with propagate_attributes(tags=[...]) currently drops the inherited tags. The merge reads only the SDK's local context key, which is not restored by header extraction. In-process nesting already appends tags.

This change seeds the merge from the existing _get_propagated_attributes_from_context(context) reader. It reuses #1919's tag decoding and preserves the reader's precedence of local context over baggage. The existing append/dedup logic and wire format are unchanged.

The regression tests cover:

  • Real W3C header serialization and extraction into a fresh context.
  • Updating the active observation and propagating the merged tags to its child.
  • Overlapping tags, order, and a tag containing a comma.
  • Restoring the inherited tags for observations created after leaving the local scope.
  • as_baggage=False: merge locally, keep outgoing upstream baggage unchanged.
  • as_baggage=True: send the merged tags to the next service.
  • Keeping local context authoritative when it differs from baggage.

The propagate_attributes docstring now states the additive behavior. The JS counterpart is langfuse/langfuse-js#959 / langfuse/langfuse-js#966.

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Refactor
  • Documentation update
  • Tooling, CI, or repo maintenance

Verification

Python 3.12.14, Windows, OTel API/SDK 1.45.0. Commands used the existing locked development environment's Python interpreter, with PYTHONPATH pointing at this isolated worktree:

# Baseline with #1919 only: 136 passed
python -m pytest tests/unit/test_propagate_attributes.py -q -o log_cli=false --tb=short --timeout=20

# Before the fix: 2 failed (False/True baggage modes), 1 passed (precedence control)
python -m pytest tests/unit/test_propagate_attributes.py -q -k 'tags_append_' -o log_cli=false --tb=short --timeout=20

# After the fix: 204 passed, 2 skipped
python -m pytest tests/unit/test_propagate_attributes.py tests/unit/test_otel.py -q -o log_cli=false --tb=short --timeout=20

ruff check .
ruff format --check langfuse/_client/propagation.py tests/unit/test_propagate_attributes.py
python -m mypy langfuse --no-error-summary
git diff --check

Lint, formatting of both changed files, and type checking passed. The test run reports one existing set_trace_io deprecation warning. An initial sandboxed baseline timed out while Windows asyncio created its socket pair; the baseline and final tests above passed outside that sandbox.

I also reran an external three-process A -> B -> C localhost HTTP probe against the final source, using the real SDK with in-memory exporters. All hops kept the same trace ID:

B operation B observation tags C observation tags
No enrichment memory, retry memory, retry
Add worker, as_baggage=False memory, retry, worker memory, retry
Add worker, as_baggage=True memory, retry, worker memory, retry, worker

No backend ingestion/UI e2e or live-provider tests were run; the defect and fix are observable at the SDK export boundary. The full unit suite was not run; verification focused on propagation and OTel tests.

Checklist

  • I self-reviewed the diff using code_review.md.
  • I added or updated tests for behavior changes.
  • I updated docs, examples, or .env.template if needed. (Updated the public propagation docstring.)
  • I did not hand-edit generated files; if generated files changed, I used the upstream regeneration path.
  • I did not commit secrets or credentials.

lets-order-some-fries and others added 2 commits October 7, 2026 16:32
propagate_attributes(tags=..., as_baggage=True) sends tags as the Python
list literal "['tag-a', 'tag-b']", because the W3C baggage propagator
writes str(value). The receiving service set that string as
langfuse.trace.tags instead of a list.

Parse tags when reading them from baggage: a JSON array or a Python list
literal, falling back to a comma split for the JS SDK's "tag-a,tag-b".
The wire format is unchanged, since Langfuse's AI gateway already parses
it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 22292d5)

This branch has not been deployed

No deployments
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(sdk-python): adding tags downstream replaces tags inherited via baggage

2 participants