fix(tracing): keep propagated tags intact across baggage headers - #1919
lets-order-some-fries wants to merge 1 commit into
Conversation
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>
| if isinstance(tags, list) and all(isinstance(tag, str) for tag in tags): | ||
| return tags |
There was a problem hiding this comment.
List-shaped tags change meaning A JS-originated single tag whose literal value is
["red","blue"] is parsed as two tags here. The downstream span records red and blue instead of the original tag, making trace tags unreliable for this valid value.
Knowledge Base Used: OpenTelemetry propagation and export
Prompt To Fix With AI
This is a comment left during a code review.
Path: langfuse/_client/propagation.py
Line: 563-564
Comment:
**List-shaped tags change meaning** A JS-originated single tag whose literal value is `["red","blue"]` is parsed as two tags here. The downstream span records `red` and `blue` instead of the original tag, making trace tags unreliable for this valid value.
**Knowledge Base Used:** [OpenTelemetry propagation and export](https://app.greptile.com/personal-org-4986/-/custom-context/knowledge-base/langfuse/langfuse-python/-/docs/opentelemetry-propagation-and-export.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Good catch on the edge case. A single tag whose text is itself a JSON array is ambiguous on the wire: the AI gateway's parse_tags reads ["red","blue"] as two tags as well, and the JS SDK's comma split already breaks that tag apart today. I kept the reader consistent with the gateway, but I'm happy to narrow it if you'd prefer.
There was a problem hiding this comment.
That’s a fair clarification. Given that the wire format is already ambiguous and the gateway’s parse_tags applies the same interpretation, keeping the reader aligned with the gateway is the least surprising behavior. The JS SDK’s comma-delimited format also cannot preserve a tag containing commas, so narrowing only the Python reader would not restore round-trip fidelity. I’m withdrawing this edge-case finding; no change is needed here.
Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.
What does this PR do?
No linked issue. I found this while reading
propagation.py.propagate_attributes(tags=[...], as_baggage=True)puts the tag list itself into baggage (langfuse/_client/propagation.py:606at 0bc5897), and OTel'sW3CBaggagePropagatorwrites every value withstr(value). The receiving service passes the decoded string straight through (propagation.py:495), so its spans getlangfuse.trace.tagsset to the string"['tag-a', 'tag-b']"instead of a list. Only tags are affected.The wire format stays, because Langfuse's AI gateway already parses this form (
parse_tagsinai-gateway/src/telemetry/context.rs, from langfuse/langfuse#17549). So the fix is read-side only: alangfuse_tagsvalue starting with[is parsed as a JSON array, then as a Python list literal, and kept only if it is a list of strings. Anything else is split on,, as the JS SDK writes it.ast.literal_evalruns no code, OTel drops baggage entries over 4096 bytes, and malformed or deeply nested input raises and falls through to the split.Tags with commas now survive a Python-to-Python hop. A JS service still splits Python-written tags on commas; that fix belongs in langfuse-js.
The existing baggage tests stay in one process, where the in-context list hides the baggage value. The new tests extract a real header into an empty context; on
mainthe first fails withassert "['tag-a', 'comma,tag']" == ('tag-a', 'comma,tag').Type of change
Verification
List the main commands you ran:
The gateway's
tests/fixtures/python-baggage.jsoncarrier now reads back as its ten tags, and the SDK writes a byte-identicallangfuse_tagsentry for them. I did not run e2e or live_provider, since unit tests cover the change without a server.Checklist
code_review.md..env.templateif needed. (not needed)🤖 Generated with Claude Code
The parsing change has a narrow tag-corruption edge case, and the repository’s import-placement requirement must be satisfied before merging.
Summary
The PR reads cross-service baggage tags as lists while leaving the Python baggage wire format unchanged.
Reviews (1) · Last reviewed commit: "fix(tracing): keep propagated tags intac..."