Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions langfuse/_client/propagation.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,12 @@ def _propagate_attributes(
validated_metadata: Dict[str, str] = {}

for key, value in metadata.items():
if _validate_string_value(value=value, key=f"metadata.{key}"):
validated_metadata[key] = value
# Coerce non-string values to strings per v3→v4 migration guide
# (LangGraph injects int/list/tuple metadata like langgraph_step)
str_value = str(value) if not isinstance(value, str) else value

Choose a reason for hiding this comment

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

P2 Badge Handle str failures when coercing metadata

Calling str(value) unconditionally for non-string metadata can now raise if a user-supplied object has a failing __str__ implementation (e.g., proxy/mock/domain objects), which causes propagate_attributes to throw and break request/span execution. Before this change, non-string metadata was dropped without raising, so this is a regression in fault tolerance for malformed metadata inputs.

Useful? React with 👍 / 👎.


if _validate_string_value(value=str_value, key=f"metadata.{key}"):
validated_metadata[key] = str_value

if validated_metadata:
context = _set_propagated_attribute(
Expand Down
46 changes: 46 additions & 0 deletions tests/test_propagate_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,52 @@ def test_all_invalid_metadata_values(self, langfuse_client, memory_exporter):
child_span, f"{LangfuseOtelSpanAttributes.TRACE_METADATA}.key2"
)

def test_non_string_metadata_values_coerced(
self, langfuse_client, memory_exporter
):
"""Verify non-string metadata values are coerced to strings, not dropped.
LangGraph injects int/list/tuple metadata (langgraph_step, langgraph_triggers,
langgraph_path) which should be coerced per the v3->v4 migration guide.
See: https://github.com/langfuse/langfuse-python/issues/1571
"""
with langfuse_client.start_as_current_observation(name="parent-span"):
with propagate_attributes(
metadata={
"langgraph_step": 3,
"langgraph_triggers": ["start:__start__"],
"langgraph_path": ("__pregel_pull", "agent"),
"string_key": "normal_value",
}
):
child = langfuse_client.start_observation(name="child-span")
child.end()

child_span = self.get_span_by_name(memory_exporter, "child-span")

# Non-string values should be coerced to strings
self.verify_span_attribute(
child_span,
f"{LangfuseOtelSpanAttributes.TRACE_METADATA}.langgraph_step",
"3",
)
self.verify_span_attribute(
child_span,
f"{LangfuseOtelSpanAttributes.TRACE_METADATA}.langgraph_triggers",
"['start:__start__']",
)
self.verify_span_attribute(
child_span,
f"{LangfuseOtelSpanAttributes.TRACE_METADATA}.langgraph_path",
"('__pregel_pull', 'agent')",
)
# String values should pass through unchanged
self.verify_span_attribute(
child_span,
f"{LangfuseOtelSpanAttributes.TRACE_METADATA}.string_key",
"normal_value",
)

def test_propagate_with_no_active_span(self, langfuse_client, memory_exporter):
"""Verify propagate_attributes works even with no active span."""
# Call propagate_attributes without creating a parent span first
Expand Down