From 7848bc74b111497b946a11c992dd19d8fbb40989 Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Mon, 29 Jun 2026 12:26:11 -0400 Subject: [PATCH 1/2] fix(scope): Drop None values from user dict in set_user When set_user is called with a dict, any keys with None values are now filtered out before storing on the scope. This prevents None user attributes from being sent to Sentry. --- sentry_sdk/scope.py | 9 ++++++++- tests/test_scope.py | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index cec5e767c3..ac8b876010 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -890,7 +890,14 @@ def user(self, value: "Optional[Dict[str, Any]]") -> None: def set_user(self, value: "Optional[Dict[str, Any]]") -> None: """Sets a user for the scope.""" - self._user = value + if value is not None: + self._user = {} + for k, v in value.items(): + if v is not None: + self._user[k] = v + else: + self._user = None + session = self.get_isolation_scope()._session if session is not None: session.update(user=value) diff --git a/tests/test_scope.py b/tests/test_scope.py index 39a162be18..684be5fb6e 100644 --- a/tests/test_scope.py +++ b/tests/test_scope.py @@ -68,6 +68,32 @@ def test_scope_flags_copy(): ] +def test_set_user(sentry_init, capture_events): + sentry_init() + events = capture_events() + + sentry_sdk.get_isolation_scope().set_user({"id": "42", "email": "bob@example.com"}) + capture_exception(NameError()) + assert events[-1]["user"] == {"id": "42", "email": "bob@example.com"} + + sentry_sdk.get_isolation_scope().set_user(None) + capture_exception(NameError()) + assert "user" not in events[-1] + + +def test_set_user_none_values_are_dropped_when_copying_to_attributes( + sentry_init, capture_events +): + sentry_init() + events = capture_events() + + sentry_sdk.get_isolation_scope().set_user( + {"email": "ada@beans.com", "username": None} + ) + capture_exception(NameError()) + assert events[-1]["user"] == {"email": "ada@beans.com"} + + def test_merging(sentry_init, capture_events): sentry_init() From 37ba0fd86e04d67f4e8a1b2403fc30ce050629bd Mon Sep 17 00:00:00 2001 From: Erica Pisani Date: Tue, 30 Jun 2026 08:56:01 -0400 Subject: [PATCH 2/2] move check to within the method --- sentry_sdk/scope.py | 14 ++++++-------- tests/test_scope.py | 27 +++++++++++++++++++-------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index ac8b876010..4fd22714cf 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -890,13 +890,7 @@ def user(self, value: "Optional[Dict[str, Any]]") -> None: def set_user(self, value: "Optional[Dict[str, Any]]") -> None: """Sets a user for the scope.""" - if value is not None: - self._user = {} - for k, v in value.items(): - if v is not None: - self._user[k] = v - else: - self._user = None + self._user = value session = self.get_isolation_scope()._session if session is not None: @@ -1760,7 +1754,11 @@ def _apply_user_attributes_to_telemetry( ("user.email", "email"), ("user.ip_address", "ip_address"), ): - if user_attribute in self._user and attribute_name not in attributes: + if ( + user_attribute in self._user + and attribute_name not in attributes + and self._user[user_attribute] is not None + ): attributes[attribute_name] = self._user[user_attribute] def _drop(self, cause: "Any", ty: str) -> "Optional[Any]": diff --git a/tests/test_scope.py b/tests/test_scope.py index 684be5fb6e..d7ce1f1ff8 100644 --- a/tests/test_scope.py +++ b/tests/test_scope.py @@ -82,16 +82,27 @@ def test_set_user(sentry_init, capture_events): def test_set_user_none_values_are_dropped_when_copying_to_attributes( - sentry_init, capture_events + sentry_init, capture_items ): - sentry_init() - events = capture_events() - - sentry_sdk.get_isolation_scope().set_user( - {"email": "ada@beans.com", "username": None} + sentry_init( + traces_sample_rate=1.0, + send_default_pii=True, + _experiments={"trace_lifecycle": "stream"}, ) - capture_exception(NameError()) - assert events[-1]["user"] == {"email": "ada@beans.com"} + items = capture_items("span") + + with sentry_sdk.traces.start_span(name="test_segment"): + sentry_sdk.get_isolation_scope().set_user( + {"email": "ada@beans.com", "username": None} + ) + capture_exception(NameError()) + + sentry_sdk.flush() + + segment = items[0].payload + + assert "user.name" not in segment["attributes"] + assert segment["attributes"]["user.email"] == "ada@beans.com" def test_merging(sentry_init, capture_events):