Skip to content
Open
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
31 changes: 30 additions & 1 deletion sentry_sdk/integrations/langchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,14 @@ def on_chat_model_start(self, serialized, messages, *, run_id, **kwargs):
elif "openai" in ai_type:
span.set_data(SPANDATA.GEN_AI_SYSTEM, "openai")

agent_name = (
sentry_sdk.get_current_scope()
._contexts.get("langchain_agent", {})
.get("agent_name")
)
if agent_name:
span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent_name)

for key, attribute in DATA_FIELDS.items():
if key in all_params and all_params[key] is not None:
set_data_normalized(span, attribute, all_params[key], unpack=False)
Expand Down Expand Up @@ -428,6 +436,14 @@ def on_tool_start(self, serialized, input_str, *, run_id, **kwargs):
if tool_description is not None:
span.set_data(SPANDATA.GEN_AI_TOOL_DESCRIPTION, tool_description)

agent_name = (
sentry_sdk.get_current_scope()
._contexts.get("langchain_agent", {})
.get("agent_name")
)
if agent_name:
span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent_name)

if should_send_default_pii() and self.include_prompts:
set_data_normalized(
span,
Expand Down Expand Up @@ -756,6 +772,9 @@ def new_invoke(self, *args, **kwargs):
name=f"invoke_agent {agent_name}" if agent_name else "invoke_agent",
origin=LangchainIntegration.origin,
) as span:
sentry_sdk.get_current_scope().set_context(
"langchain_agent", {"agent_name": agent_name, "tools": tools}
)
if agent_name:
span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent_name)

Expand Down Expand Up @@ -794,6 +813,8 @@ def new_invoke(self, *args, **kwargs):
):
set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, output)

sentry_sdk.get_current_scope().remove_context("langchain_agent")
Copy link

Choose a reason for hiding this comment

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

Bug: Context Leak in Exception Handling

Context leak in _wrap_agent_executor_invoke: if f(self, *args, **kwargs) throws an exception after the context is set (line 775-777), the remove_context call at line 816 will never execute, causing the "langchain_agent" context to persist beyond its intended scope.

Fix in Cursor Fix in Web


return result

return new_invoke
Expand All @@ -814,11 +835,15 @@ def new_stream(self, *args, **kwargs):

span = start_span_function(
op=OP.GEN_AI_INVOKE_AGENT,
name=f"invoke_agent {agent_name}".strip(),
name=f"invoke_agent {agent_name}" if agent_name else "invoke_agent",
origin=LangchainIntegration.origin,
)
span.__enter__()

sentry_sdk.get_current_scope().set_context(
"langchain_agent", {"agent_name": agent_name, "tools": tools}
)

if agent_name:
span.set_data(SPANDATA.GEN_AI_AGENT_NAME, agent_name)

Expand Down Expand Up @@ -868,6 +893,8 @@ def new_iterator():
):
set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, output)

sentry_sdk.get_current_scope().remove_context("langchain_agent")
Copy link

Choose a reason for hiding this comment

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

Bug: Context Leak in Exception Handling

Context leak in _wrap_agent_executor_stream: if f(self, *args, **kwargs) throws an exception after the context is set (line 843-845), the iterators containing the remove_context calls (lines 896, 917) are never created, causing the "langchain_agent" context to leak permanently.

Fix in Cursor Fix in Web


span.__exit__(None, None, None)

async def new_iterator_async():
Expand All @@ -887,6 +914,8 @@ async def new_iterator_async():
):
set_data_normalized(span, SPANDATA.GEN_AI_RESPONSE_TEXT, output)

sentry_sdk.get_current_scope().remove_context("langchain_agent")

span.__exit__(None, None, None)

if str(type(result)) == "<class 'async_generator'>":
Expand Down