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
@@ -667,7 +676,7 @@ def single_exception_from_error_tuple(
667676 source = None , # type: Optional[str]
668677 full_stack = None , # type: Optional[list[dict[str, Any]]]
669678):
670- # type: (...) -> Dict[str, Any]
679+ # type: (...) -> Annotated[ Dict[str, Any] ]
671680 """
672681 Creates a dict that goes into the events `exception.values` list and is ingestible by Sentry.
673682
@@ -732,10 +741,15 @@ 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+ for tb , _ in zip ( iter_stacks (tb ), range ( MAX_STACK_FRAMES + 1 ) )
736745 ] # type: List[Dict[str, Any]]
737746
738- if frames :
747+ if len (frames ) > MAX_STACK_FRAMES :
748+ exception_value ["stacktrace" ] = AnnotatedValue .removed_because_over_size_limit (
749+ value = None
750+ )
751+
752+ elif frames :
739753 if not full_stack :
740754 new_frames = frames
741755 else :
@@ -798,7 +812,7 @@ def exceptions_from_error(
798812 source = None , # type: Optional[str]
799813 full_stack = None , # type: Optional[list[dict[str, Any]]]
800814):
801- # type: (...) -> Tuple[int, List[Dict[str, Any]]]
815+ # type: (...) -> Tuple[int, List[Annotated[ Dict[str, Any] ]]]
802816 """
803817 Creates the list of exceptions.
804818 This can include chained exceptions and exceptions from an ExceptionGroup.
@@ -894,7 +908,7 @@ def exceptions_from_error_tuple(
894908 mechanism = None , # type: Optional[Dict[str, Any]]
895909 full_stack = None , # type: Optional[list[dict[str, Any]]]
896910):
897- # type: (...) -> List[Dict[str, Any]]
911+ # type: (...) -> List[Annotated[ Dict[str, Any] ]]
898912 exc_type , exc_value , tb = exc_info
899913
900914 is_exception_group = BaseExceptionGroup is not None and isinstance (
@@ -941,7 +955,7 @@ def to_string(value):
941955
942956
943957def iter_event_stacktraces (event ):
944- # type: (Event) -> Iterator[Dict[str, Any]]
958+ # type: (Event) -> Iterator[Annotated[ Dict[str, Any] ]]
945959 if "stacktrace" in event :
946960 yield event ["stacktrace" ]
947961 if "threads" in event :
@@ -950,20 +964,26 @@ def iter_event_stacktraces(event):
950964 yield thread ["stacktrace" ]
951965 if "exception" in event :
952966 for exception in event ["exception" ].get ("values" ) or ():
953- if "stacktrace" in exception :
967+ if isinstance ( exception , dict ) and "stacktrace" in exception :
954968 yield exception ["stacktrace" ]
955969
956970
957971def iter_event_frames (event ):
958972 # type: (Event) -> Iterator[Dict[str, Any]]
959973 for stacktrace in iter_event_stacktraces (event ):
974+ if isinstance (stacktrace , AnnotatedValue ):
975+ stacktrace = stacktrace .value or {}
976+
960977 for frame in stacktrace .get ("frames" ) or ():
961978 yield frame
962979
963980
964981def handle_in_app (event , in_app_exclude = None , in_app_include = None , project_root = None ):
965982 # type: (Event, Optional[List[str]], Optional[List[str]], Optional[str]) -> Event
966983 for stacktrace in iter_event_stacktraces (event ):
984+ if isinstance (stacktrace , AnnotatedValue ):
985+ stacktrace = stacktrace .value or {}
986+
967987 set_in_app_in_frames (
968988 stacktrace .get ("frames" ),
969989 in_app_exclude = in_app_exclude ,
0 commit comments