7777FALSY_ENV_VALUES = frozenset (("false" , "f" , "n" , "no" , "off" , "0" ))
7878TRUTHY_ENV_VALUES = frozenset (("true" , "t" , "y" , "yes" , "on" , "1" ))
7979
80+ MAX_STACK_FRAMES = 2000
81+ """Maximum number of stack frames to send to Sentry.
82+
83+ If we have more than this number of stack frames, we will stop processing
84+ the stacktrace to avoid getting stuck in a long-lasting loop. This value
85+ exceeds the default sys.getrecursionlimit() of 1000, so users will only
86+ be affected by this limit if they have a custom recursion limit.
87+ """
88+
8089
8190def env_to_bool (value , * , strict = False ):
8291 # type: (Any, Optional[bool]) -> bool | None
@@ -732,10 +741,23 @@ def single_exception_from_error_tuple(
732741 max_value_length = max_value_length ,
733742 custom_repr = custom_repr ,
734743 )
735- for tb in iter_stacks (tb )
744+ # Process at most MAX_STACK_FRAMES + 1 frames, to avoid hanging on
745+ # processing a super-long stacktrace.
746+ for tb , _ in zip (iter_stacks (tb ), range (MAX_STACK_FRAMES + 1 ))
736747 ] # type: List[Dict[str, Any]]
737748
738- if frames :
749+ if len (frames ) > MAX_STACK_FRAMES :
750+ # If we have more frames than the limit, we remove the stacktrace completely.
751+ # We don't trim the stacktrace here because we have not processed the whole
752+ # thing (see above, we stop at MAX_STACK_FRAMES + 1). Normally, Relay would
753+ # intelligently trim by removing frames in the middle of the stacktrace, but
754+ # since we don't have the whole stacktrace, we can't do that. Instead, we
755+ # drop the entire stacktrace.
756+ exception_value ["stacktrace" ] = AnnotatedValue .removed_because_over_size_limit (
757+ value = None
758+ )
759+
760+ elif frames :
739761 if not full_stack :
740762 new_frames = frames
741763 else :
@@ -941,7 +963,7 @@ def to_string(value):
941963
942964
943965def iter_event_stacktraces (event ):
944- # type: (Event) -> Iterator[Dict[str, Any]]
966+ # type: (Event) -> Iterator[Annotated[ Dict[str, Any] ]]
945967 if "stacktrace" in event :
946968 yield event ["stacktrace" ]
947969 if "threads" in event :
@@ -950,20 +972,26 @@ def iter_event_stacktraces(event):
950972 yield thread ["stacktrace" ]
951973 if "exception" in event :
952974 for exception in event ["exception" ].get ("values" ) or ():
953- if "stacktrace" in exception :
975+ if isinstance ( exception , dict ) and "stacktrace" in exception :
954976 yield exception ["stacktrace" ]
955977
956978
957979def iter_event_frames (event ):
958980 # type: (Event) -> Iterator[Dict[str, Any]]
959981 for stacktrace in iter_event_stacktraces (event ):
982+ if isinstance (stacktrace , AnnotatedValue ):
983+ stacktrace = stacktrace .value or {}
984+
960985 for frame in stacktrace .get ("frames" ) or ():
961986 yield frame
962987
963988
964989def handle_in_app (event , in_app_exclude = None , in_app_include = None , project_root = None ):
965990 # type: (Event, Optional[List[str]], Optional[List[str]], Optional[str]) -> Event
966991 for stacktrace in iter_event_stacktraces (event ):
992+ if isinstance (stacktrace , AnnotatedValue ):
993+ stacktrace = stacktrace .value or {}
994+
967995 set_in_app_in_frames (
968996 stacktrace .get ("frames" ),
969997 in_app_exclude = in_app_exclude ,
0 commit comments