Repository: getsentry/sentry

Issue:
Incorrect data in events when adding large integers to context data 
Sentry is treating big values differently in tags and context causing some values to be in correct after event processing. Below is an example event I sent using the ruby SDK, the same behaviour happens in Python as well: 

Script:
```
::Sentry.init do |config|
  config.dsn = "_MY_DSN_"
end

Sentry.set_tags('bigNumber': 608548899684111178)
Sentry.configure_scope do |scope|
  scope.set_context(
    'arguments',
    {
      name: 'A big value',
      age: 608548899684111178,
    }
  )
end

Sentry.capture_message("le big number test")
```

I expect to see both tag and context showing the same value, but in the context session it is showing as `608548899684111200` This is a common issue with large integer/floating point in Node.  

A possible workaround is to add the value as a string when it is added manually, but it is not applicable when the data is automatically added by the SDK.

![Screenshot 2024-03-11 at 11 48 50](https://github.com/getsentry/relay/assets/64906376/1d25a2b3-d369-4b0c-bb80-9cf038fde3df)



Discussion:
Hi, thanks for reporting. I have checked Relay with some events and transactions and it works as expected -- produced events have the same numbers in the tag value and context. I can also see the mismatched context value when looking at the issue in sentry, so I confirm there's a bug somewhere.

I'm transferring this issue to `sentry` to find the appropriate team for triage.
Assigning to @getsentry/support for [routing](https://open.sentry.io/triage/#2-route) ⏲️
Routing to @getsentry/product-owners-issues for [triage](https://develop.sentry.dev/processing-tickets/#3-triage) ⏲️
I was able to reproduce this as well, we are correctly storing the real value sent to us at every stage, it only errors when displaying the value on to the frontend. It seems to come from JSON parsing integers values in the API response that exceed `Number.MAX_SAFE_INTEGER` is the problem here.

I have filed the bug, and the fix will likely be in stringifying the value for these contexts at some stage (the way we do with the tag).

Resolving patch:
diff --git a/src/sentry/interfaces/contexts.py b/src/sentry/interfaces/contexts.py
--- a/src/sentry/interfaces/contexts.py
+++ b/src/sentry/interfaces/contexts.py
@@ -93,6 +93,9 @@ def __init__(self, alias, data):
             # we still want to display the info the UI
             if value is not None:
                 ctx_data[force_str(key)] = value
+            # Numbers exceeding 15 place values will be converted to strings to avoid rendering issues
+            if isinstance(value, (int, float)) and len(str_value := force_str(value)) > 15:
+                ctx_data[force_str(key)] = str_value
         self.data = ctx_data
 
     def to_json(self):


Test patch:
diff --git a/tests/sentry/event_manager/interfaces/test_contexts.py b/tests/sentry/event_manager/interfaces/test_contexts.py
--- a/tests/sentry/event_manager/interfaces/test_contexts.py
+++ b/tests/sentry/event_manager/interfaces/test_contexts.py
@@ -97,3 +97,28 @@ def test_gpu(make_ctx_snapshot):
     make_ctx_snapshot(
         {"gpu": {"name": "AMD Radeon Pro 560", "vendor_name": "Apple", "version": "Metal"}}
     )
+
+
+def test_large_numbers():
+    data = {
+        "large_numbers": {
+            "decimal_number": 123456.789,
+            "number": 123456789,
+            "negative_number": -123456789,
+            "big_decimal_number": 123456789.123456789,
+            "big_number": 123456789123456789,
+            "big_negative_number": -123456789123456789,
+        }
+    }
+    numeric_keys = {"decimal_number", "number", "negative_number"}
+    string_keys = {"big_decimal_number", "big_number", "big_negative_number"}
+
+    mgr = EventManager(data={"contexts": data})
+    mgr.normalize()
+    evt = eventstore.backend.create_event(data=mgr.get_data())
+    interface = evt.interfaces.get("contexts")
+    ctx_data = interface.to_json()["large_numbers"]
+    for key in numeric_keys:
+        assert isinstance(ctx_data[key], (int, float))
+    for key in string_keys:
+        assert isinstance(ctx_data[key], str)