traceloop / traceloop/openllmetry

πŸ› Bug Report: list-valued association properties crash metrics, and silently drop gen_ai.output.messages

Open
#4,436 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
7.4k
Forks
1.1k
Avg merge
8d 14h
Merged PRs (30d)
2

Description

Which component is this bug for?

OpenAI Instrumentation

πŸ“œ Description
Description

When association properties (baggage set via set_association_properties() / OTel context, e.g. by the LangChain/LangGraph instrumentation's langgraph_triggers) contain a list value, metrics_common_attributes() in traceloop/sdk/tracing/tracing.py copies that value verbatim into the metrics attribute dict:

association_properties = get_value("association_properties")
if association_properties is not None:
    for key, value in association_properties.items():
        common_attributes[f"{SpanAttributes.TRACELOOP_ASSOCIATION_PROPERTIES}.{key}"] = value

This dict is registered as get_common_metrics_attributes for the OpenAI (and Anthropic/LiteLLM) instrumentor and merged into every Counter.record(...) call's attributes in _set_chat_metrics β†’ _set_token_counter_metrics (opentelemetry-instrumentation-openai). OTel's metrics SDK requires attribute values to be scalar/hashable, since it uses frozenset(attributes.items()) to key aggregation. A list value causes an unhandled TypeError: unhashable type: 'list'.

Because this happens inside _handle_response in opentelemetry/instrumentation/openai/shared/chat_wrappers.py, which is wrapped by a single @dont_throw, the exception is silently swallowed (only a DEBUG log is emitted) and every response-side span attribute set after that point is lost β€” gen_ai.response.model, gen_ai.usage.*, and, most importantly, gen_ai.output.messages. Only gen_ai.input.messages (set earlier, before the response arrives) survives. Users see prompts/inputs in their traces but no completions/outputs, with no visible error unless they specifically enable DEBUG logging for opentelemetry.instrumentation.openai.

πŸ‘Ÿ Reproduction steps
  1. Install traceloop-sdk with LangGraph instrumentation enabled (opentelemetry-instrumentation-langchain picks this up automatically) and opentelemetry-instrumentation-openai.
  2. Run a LangGraph graph with more than one edge/trigger into a node so that LangGraph's callback sets langgraph_triggers (a list[str]) as an association property/baggage value for that node's span β€” e.g. a node reachable via ["branch:to:some_node"].
  3. Inside that LangGraph node, call openai.AsyncOpenAI().chat.completions.create(...) (or the sync equivalent) with Traceloop.init() initialized normally (metrics enabled, the default).
  4. Export/inspect the resulting openai.chat span's attributes.

Minimal repro sketch (no LangGraph dependency needed β€” directly setting the association property reproduces it):

from traceloop.sdk import Traceloop
from traceloop.sdk.tracing.tracing import set_association_properties

Traceloop.init(app_name="repro", disable_batch=True)
set_association_properties({"langgraph_triggers": ["branch:to:some_node"]})

# any chat.completions.create(...) call through an instrumented AsyncOpenAI/OpenAI client

Enabling logging.getLogger("opentelemetry.instrumentation.openai").setLevel(logging.DEBUG) surfaces:

OpenLLMetry failed to trace in _handle_response, error: Traceback (most recent call last):
  ...
  File ".../opentelemetry/instrumentation/openai/shared/chat_wrappers.py", line 417, in _set_token_counter_metrics
    token_counter.record(val, attributes=attributes_with_token_type)
  ...
  File ".../opentelemetry/sdk/metrics/_internal/_view_instrument_match.py", line 101, in consume_measurement
    aggr_key = frozenset(attributes.items())
TypeError: unhashable type: 'list'
πŸ‘ Expected behavior
  • Association-property values that aren't valid OTel metric attribute types (i.e. anything other than str, bool, int, float, or a homogeneous sequence of those) should be sanitized (e.g. JSON-encoded to a string, or dropped with a warning) before being merged into get_common_metrics_attributes()'s output β€” mirroring how such values are already made safe when written to span attributes.
  • At minimum, a single malformed metrics attribute should never be able to silently abort span attribute capture for the rest of the response (gen_ai.output.messages, gen_ai.response.model, gen_ai.usage.*). Metrics recording and span attribute setting are logically independent concerns and a failure in one should not cascade into the other.
πŸ‘Ž Actual Behavior with Screenshots
  • _set_chat_metrics (called near the top of _handle_response) raises TypeError: unhashable type: 'list' when merging association properties containing a list value into metric attributes.
  • Because _handle_response is wrapped by a single @dont_throw, the exception aborts the rest of the function silently β€” no error is raised to the caller, and only a DEBUG-level log records it.
  • As a result, gen_ai.output.messages, gen_ai.response.model, and all gen_ai.usage.* attributes are missing from the span, while gen_ai.input.messages (set earlier, before the response) is present β€” making it look like only prompts are captured and completions are silently dropped, with no indication of why unless DEBUG logging happens to be enabled.
πŸ€– Python Version

3.13

πŸ“ƒ Provide any additional context for the Bug.

No response

πŸ‘€ Have you spent some time to check if this bug has been raised before?
  • I checked and didn't find similar issue
Are you willing to submit PR?

https://github.com/traceloop/openllmetry/pull/4437

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up β€” it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in traceloop/sdk/tracing/tracing.py at metrics_common_attributes(), then read _handle_response and _set_token_counter_metrics in opentelemetry/instrumentation/openai/shared/chat_wrappers.py. Reproduce the list-valued association-property case with metrics enabled and verify that metric recording no longer prevents gen_ai.output.messages, response.model, and usage attributes from being captured.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
observability-sre
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.