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
3 changes: 2 additions & 1 deletion sentry_sdk/integrations/langchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,8 @@ def on_llm_end(
finish_reason = generation.generation_info.get("finish_reason")
if finish_reason is not None:
span.set_data(
SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS, finish_reason
SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS,
[finish_reason],
)
except AttributeError:
pass
Comment on lines 554 to 561
Copy link

Choose a reason for hiding this comment

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

Bug: The on_chat_model_end callback closes the span before on_llm_end can run, preventing the finish_reason from being set on chat model spans.
Severity: MEDIUM

Suggested Fix

The logic to set the finish_reason should be moved from on_llm_end to on_chat_model_end for chat model spans. This ensures the attribute is set before the span is closed and removed from the span_map.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: sentry_sdk/integrations/langchain.py#L554-L561

Potential issue: For chat models, LangChain invokes both `on_chat_model_end` and
`on_llm_end` for the same `run_id`. The `on_chat_model_end` callback calls `_exit_span`,
which closes the span and removes it from the internal `span_map`. Subsequently, when
`on_llm_end` executes, it cannot find the span for the given `run_id` and returns early.
This prevents the `GEN_AI_RESPONSE_FINISH_REASONS` attribute from being set on the span,
meaning chat model spans will be missing the `finish_reason` in production environments.
The existing tests may pass because the mocks do not replicate this dual-callback
behavior.

Did we get this right? 👍 / 👎 to inform future reviews.

Expand Down
6 changes: 6 additions & 0 deletions tests/integrations/langchain/test_langchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,12 @@ def test_langchain_agent(
f"and include_prompts={include_prompts}"
)

# Verify finish_reasons is always an array of strings
assert chat_spans[0]["data"][SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS] == [
"function_call"
]
assert chat_spans[1]["data"][SPANDATA.GEN_AI_RESPONSE_FINISH_REASONS] == ["stop"]

# Verify that available tools are always recorded regardless of PII settings
for chat_span in chat_spans:
span_data = chat_span.get("data", {})
Expand Down
Loading