Skip to content

Commit 3fbb8bd

Browse files
fix(langgraph): Ignore GraphBubbleUp exceptions
1 parent 883e585 commit 3fbb8bd

3 files changed

Lines changed: 43 additions & 4 deletions

File tree

sentry_sdk/integrations/langchain.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ class LangchainIntegration(Integration):
210210
identifier = "langchain"
211211
origin = f"auto.ai.{identifier}"
212212

213+
_ignored_exceptions: "set[type[Exception]]" = set()
214+
213215
def __init__(
214216
self: "LangchainIntegration",
215217
include_prompts: bool = True,
@@ -262,17 +264,22 @@ def gc_span_map(self) -> None:
262264
self._exit_span(span, run_id)
263265

264266
def _handle_error(self, run_id: "UUID", error: "Any") -> None:
267+
is_ignored = isinstance(error, tuple(LangchainIntegration._ignored_exceptions))
268+
265269
with capture_internal_exceptions():
266270
if not run_id or run_id not in self.span_map:
267271
return
268272

269273
span = self.span_map[run_id]
270274

271-
sentry_sdk.capture_exception(
272-
error, span._scope if isinstance(span, StreamedSpan) else span.scope
273-
)
275+
if is_ignored:
276+
span.__exit__(None, None, None)
277+
else:
278+
sentry_sdk.capture_exception(
279+
error, span._scope if isinstance(span, StreamedSpan) else span.scope
280+
)
281+
span.__exit__(type(error), error, error.__traceback__)
274282

275-
span.__exit__(type(error), error, error.__traceback__)
276283
del self.span_map[run_id]
277284

278285
def _normalize_langchain_message(self, message: "BaseMessage") -> "Any":

sentry_sdk/integrations/langgraph.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
)
1111
from sentry_sdk.consts import OP, SPANDATA
1212
from sentry_sdk.integrations import DidNotEnable, Integration
13+
14+
# This is fine because langgraph depends on langchain-base, and LangchainIntegration only imports from langchain-base.
15+
from sentry_sdk.integrations.langchain import LangchainIntegration
1316
from sentry_sdk.scope import should_send_default_pii
1417
from sentry_sdk.traces import StreamedSpan
1518
from sentry_sdk.tracing_utils import (
@@ -19,6 +22,7 @@
1922
from sentry_sdk.utils import safe_serialize
2023

2124
try:
25+
from langgraph.errors import GraphBubbleUp
2226
from langgraph.graph import StateGraph
2327
from langgraph.pregel import Pregel
2428
except ImportError:
@@ -34,6 +38,7 @@ def __init__(self: "LanggraphIntegration", include_prompts: bool = True) -> None
3438

3539
@staticmethod
3640
def setup_once() -> None:
41+
LangchainIntegration._ignored_exceptions.add(GraphBubbleUp)
3742
# LangGraph lets users create agents using a StateGraph or the Functional API.
3843
# StateGraphs are then compiled to a CompiledStateGraph. Both CompiledStateGraph and
3944
# the functional API execute on a Pregel instance. Pregel is the runtime for the graph

tests/integrations/langgraph/test_langgraph.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
from unittest.mock import MagicMock, patch
44

55
import pytest
6+
from langchain_core.language_models.chat_models import BaseChatModel
7+
from langchain_core.messages import HumanMessage
8+
from langchain_core.outputs import ChatResult
9+
from langgraph.errors import GraphBubbleUp
610

711
import sentry_sdk
812
from sentry_sdk import start_transaction
@@ -125,6 +129,15 @@ async def ainvoke(self, state, config=None):
125129
return {"messages": [MockMessage("Async Pregel response")]}
126130

127131

132+
class InterruptingChatModel(BaseChatModel):
133+
@property
134+
def _llm_type(self) -> str:
135+
return "interrupting-chat-model"
136+
137+
def _generate(self, messages, stop=None, run_manager=None, **kwargs) -> ChatResult:
138+
raise GraphBubbleUp("interrupt")
139+
140+
128141
def test_langgraph_integration_init():
129142
"""Test LanggraphIntegration initialization with different parameters."""
130143
integration = LanggraphIntegration()
@@ -2104,3 +2117,17 @@ def original_invoke(self, *args, **kwargs):
21042117
assert len(parsed_messages) == 1
21052118
assert "small message 5" in str(parsed_messages[0])
21062119
assert tx["_meta"]["spans"]["0"]["data"]["gen_ai.request.messages"][""]["len"] == 5
2120+
2121+
2122+
def test_graph_bubble_up_ignored(sentry_init, capture_events):
2123+
sentry_init(
2124+
integrations=[LanggraphIntegration()],
2125+
)
2126+
2127+
events = capture_events()
2128+
2129+
model = InterruptingChatModel()
2130+
with pytest.raises(GraphBubbleUp):
2131+
model.invoke([HumanMessage(content="hi")])
2132+
2133+
assert len(events) == 0

0 commit comments

Comments
 (0)