<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: FrankFan</title>
    <description>The latest articles on DEV Community by FrankFan (@frankcode).</description>
    <link>https://dev.to/frankcode</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1295265%2F08d30100-22df-4d06-9edb-375cadd92445.png</url>
      <title>DEV Community: FrankFan</title>
      <link>https://dev.to/frankcode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/frankcode"/>
    <language>en</language>
    <item>
      <title>The 114KB Span Attribute That Hid Our LCP Data</title>
      <dc:creator>FrankFan</dc:creator>
      <pubDate>Fri, 22 May 2026 10:34:25 +0000</pubDate>
      <link>https://dev.to/frankcode/the-114kb-span-attribute-that-hid-our-lcp-data-3a9h</link>
      <guid>https://dev.to/frankcode/the-114kb-span-attribute-that-hid-our-lcp-data-3a9h</guid>
      <description>&lt;h2&gt;
  
  
  A React Native WebView debugging story about LCP, data URLs, and trace attributes
&lt;/h2&gt;

&lt;p&gt;We recently hit a confusing Sentry performance issue in a React Native app:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The LCP transaction existed, but Trace Explorer could not find it by the attributes we attached to it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The culprit was one span attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lcpUrl = data:image/png;base64,...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In one iOS sample, that value was roughly &lt;strong&gt;114KB&lt;/strong&gt; before Sentry normalized it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The symptom
&lt;/h2&gt;

&lt;p&gt;We measure WebView page performance by reporting custom Sentry transactions for FCP and LCP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;page&amp;gt; (FCP)
&amp;lt;page&amp;gt; (LCP)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both use the same operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ui.web_page_load
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And both include attributes we use for grouping and filtering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metricInfo
pageTitle
pageUrl
host
path
durationMs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The strange part: Sentry Transaction Summary could show the LCP transaction, but Trace Explorer could not find the same data when filtering by attributes such as &lt;code&gt;metricInfo&lt;/code&gt;, &lt;code&gt;pageTitle&lt;/code&gt;, or &lt;code&gt;path&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;FCP worked. Android worked. iOS LCP did not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The raw event told the story
&lt;/h2&gt;

&lt;p&gt;After pulling the raw event from the Sentry API, the LCP transaction was clearly there. The transaction name was correct.&lt;/p&gt;

&lt;p&gt;But the trace attributes were not complete. Some diagnostic fields were present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;durationMs
host
lcpElement
lcpUrl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But several fields required for dashboard queries were missing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metricInfo
pageTitle
pageUrl
path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The suspicious field was &lt;code&gt;lcpUrl&lt;/code&gt;. On iOS, it was not a normal URL. It was a base64 image data URI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data:image/png;base64,...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sentry marked the oversized value as limited. After that, the event still existed, but the attributes we depended on for aggregation were not queryable in the way we expected.&lt;/p&gt;

&lt;p&gt;That explains the apparent contradiction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transaction Summary could still find the transaction by name.&lt;/li&gt;
&lt;li&gt;Trace Explorer could not find it by the missing attributes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Android looked fine
&lt;/h2&gt;

&lt;p&gt;This part was easy to misread. The Android data looked healthy, so it was tempting to assume the instrumentation was fine.&lt;/p&gt;

&lt;p&gt;It was not.&lt;/p&gt;

&lt;p&gt;In our production samples, the same &lt;code&gt;lcpUrl&lt;/code&gt; field looked very different by platform:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;lcpUrl&lt;/code&gt; length&lt;/th&gt;
&lt;th&gt;Attribute query fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;iOS WebView&lt;/td&gt;
&lt;td&gt;about 114KB&lt;/td&gt;
&lt;td&gt;missing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android WebView&lt;/td&gt;
&lt;td&gt;100 characters&lt;/td&gt;
&lt;td&gt;present&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To isolate the difference, we built a small WebView test page with a large base64 image as the LCP candidate. The full image data URI was about 1.6MB.&lt;/p&gt;

&lt;p&gt;On Android, the DOM and bridge could still carry the full string, but the LCP entry itself exposed only a 100-character URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DOM img.src.length            = about 1.6MB
Android bridge received value = about 1.6MB
PerformanceObserver entry.url = 100 characters
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So Android was not safe because our telemetry model was good. It was safe because Chromium WebView had already returned a short value for &lt;code&gt;entry.url&lt;/code&gt; before we sent it to Sentry.&lt;/p&gt;

&lt;p&gt;iOS Safari WebView returned the full data URI. That may be a reasonable browser behavior, but it was operationally dangerous for telemetry.&lt;/p&gt;

&lt;p&gt;I would treat the Android behavior as an implementation detail, not a contract. Application code should not rely on a browser silently shortening a dangerous value.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;We removed &lt;code&gt;lcpUrl&lt;/code&gt; and &lt;code&gt;lcpElement&lt;/code&gt; from the Sentry span attributes.&lt;/p&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;reportWebSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LCP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lcpValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lcpElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;lcpUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;reportWebSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LCP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lcpValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We kept only small, stable attributes that are useful for grouping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metricInfo
pageTitle
pageUrl
host
path
durationMs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After removing the oversized fields, LCP appeared correctly in the dashboard query again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;p&gt;Observability data needs stricter rules than ordinary application data.&lt;/p&gt;

&lt;p&gt;A field is not safe just because the browser exposes it. Before sending it as a span attribute, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is it bounded in size?&lt;/li&gt;
&lt;li&gt;Is it low-cardinality?&lt;/li&gt;
&lt;li&gt;Does it help answer a real production question?&lt;/li&gt;
&lt;li&gt;Could one bad value make the event harder to index or query?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For WebView performance telemetry, these are good attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metricInfo = LCP
pageTitle = &amp;lt;stable page name&amp;gt;
host = example.com
path = /some/path
durationMs = 3020
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are dangerous attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lcpUrl = data:image/png;base64,...
outerHTML = &amp;lt;large DOM subtree&amp;gt;
requestBody = ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dashboard was not wrong. The telemetry model was wrong.&lt;/p&gt;

&lt;p&gt;And in this case, deleting two fields made the metric visible again.&lt;/p&gt;

</description>
      <category>observability</category>
      <category>sentry</category>
      <category>reactnative</category>
      <category>webperf</category>
    </item>
    <item>
      <title>The 114KB Span Attribute That Hid Our LCP Data</title>
      <dc:creator>FrankFan</dc:creator>
      <pubDate>Fri, 22 May 2026 10:21:26 +0000</pubDate>
      <link>https://dev.to/frankcode/the-114kb-span-attribute-that-hid-our-lcp-data-3291</link>
      <guid>https://dev.to/frankcode/the-114kb-span-attribute-that-hid-our-lcp-data-3291</guid>
      <description>&lt;h2&gt;
  
  
  A React Native WebView debugging story about LCP, data URLs, and trace attributes
&lt;/h2&gt;

&lt;p&gt;We recently ran into a strange Sentry performance issue in a React Native app.&lt;/p&gt;

&lt;p&gt;The short version:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Our LCP transaction existed in Sentry, but we could not query it by the attributes we attached to it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That sounded contradictory at first. If the transaction was there, why could not Trace Explorer find it?&lt;/p&gt;

&lt;p&gt;The answer turned out to be a single span attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lcpUrl = data:image/png;base64,...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In one iOS event, that value was about &lt;strong&gt;114KB&lt;/strong&gt; before Sentry normalized it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The symptom
&lt;/h2&gt;

&lt;p&gt;We were measuring WebView performance inside a React Native app. For each WebView page load, we reported two custom Sentry transactions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Online Customer Support (FCP)
Online Customer Support (LCP)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both transactions used the same operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ui.web_page_load
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we attached searchable attributes such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metricInfo = FCP | LCP
pageTitle
pageUrl
host
path
durationMs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Sentry Transaction Summary, the LCP transaction was visible. We could open sampled events and see slow LCP data for the page.&lt;/p&gt;

&lt;p&gt;But in Trace Explorer, this query returned no iOS LCP rows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;span.op:ui.web_page_load metricInfo:LCP os.name:iOS pageTitle:"Online Customer Support"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;FCP worked. Android worked. iOS LCP did not.&lt;/p&gt;

&lt;p&gt;That is the kind of bug that makes you distrust the dashboard before you distrust your own instrumentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we found in the raw event
&lt;/h2&gt;

&lt;p&gt;When we pulled the raw event from the Sentry API, the iOS LCP transaction existed. The transaction name was correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Online Customer Support (LCP)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the trace data was not what we expected. The event still had fields like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;durationMs
host
lcpElement
lcpUrl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But important fields we expected to query by were missing from the indexed span data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metricInfo
pageTitle
pageUrl
path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The suspicious field was &lt;code&gt;lcpUrl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For that iOS LCP event, &lt;code&gt;lcpUrl&lt;/code&gt; was not a normal network URL. It was a base64 data URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data:image/png;base64,...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sentry showed the value as truncated, and the event metadata indicated the original value was much larger. In our case, the original attribute value was around &lt;strong&gt;114KB&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The production difference: iOS sent 114KB, Android sent 100 characters
&lt;/h2&gt;

&lt;p&gt;This was the detail that made the investigation click.&lt;/p&gt;

&lt;p&gt;In production, the same WebView LCP instrumentation behaved very differently across platforms:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;iOS&lt;/th&gt;
&lt;th&gt;Android&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;lcpUrl&lt;/code&gt; original length&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;114,130 chars&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100 chars&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sentry &lt;code&gt;_meta&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Saved as 7,683 chars with &lt;code&gt;!limit&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;metricInfo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing&lt;/td&gt;
&lt;td&gt;Present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pageTitle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing&lt;/td&gt;
&lt;td&gt;Present&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pageUrl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing&lt;/td&gt;
&lt;td&gt;Present&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The iOS LCP transaction was real, but it carried an enormous &lt;code&gt;lcpUrl&lt;/code&gt; value. Sentry normalized the oversized field, and the attributes we depended on for aggregation were not available in Explore.&lt;/p&gt;

&lt;p&gt;That explains the contradiction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transaction Summary could still find the event by transaction name.&lt;/li&gt;
&lt;li&gt;Explore could not find it by attributes like &lt;code&gt;metricInfo&lt;/code&gt;, &lt;code&gt;pageTitle&lt;/code&gt;, or &lt;code&gt;path&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Android looked fine because its &lt;code&gt;entry.url&lt;/code&gt; value was already short before we sent it to Sentry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Android looked fine
&lt;/h2&gt;

&lt;p&gt;We built a small WebView test page to isolate where the truncation happened.&lt;/p&gt;

&lt;p&gt;The test page generated a real 800x500 PNG, embedded it as a base64 data URI, and made it the LCP candidate. The full data URI was &lt;strong&gt;1,601,014 characters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;On Android, the results were:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DOM img.src.length              = 1,601,014
AndroidBridge.postLcpUrl(url)   = 1,601,014
PerformanceObserver entry.url   = 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That told us the React Native bridge was not truncating the value. A normal JavaScript-to-Android bridge could receive the full 1.6MB string.&lt;/p&gt;

&lt;p&gt;The 100-character value came from &lt;code&gt;PerformanceObserver&lt;/code&gt; / Chromium before our injected script sent anything to React Native.&lt;/p&gt;

&lt;p&gt;On iOS Safari WebView, &lt;code&gt;entry.url&lt;/code&gt; kept the full data URI. That is closer to the literal resource URL, but in this case it was exactly what made the telemetry event dangerous.&lt;/p&gt;

&lt;p&gt;The platform chain looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iOS Safari WebView
  PerformanceObserver -&amp;gt; entry.url = full base64 URI
    -&amp;gt; postMessage to React Native
      -&amp;gt; Sentry span attribute
        -&amp;gt; oversized field normalized
          -&amp;gt; aggregation attributes missing in Explore

Android Chromium WebView
  PerformanceObserver -&amp;gt; entry.url = 100 characters
    -&amp;gt; Sentry span attributes stay small
      -&amp;gt; dashboard query works
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also found Chromium source paths that use a 100-character URL elision pattern for internal presentation/logging-style output. I would treat this as an implementation detail, not a Web API contract. It explained our production data, but it is not something application logic should depend on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why did LCP have a data URL?
&lt;/h2&gt;

&lt;p&gt;The browser Largest Contentful Paint API can expose information about the element that became the LCP candidate. Depending on the content, the entry may include a URL-like value for the resource.&lt;/p&gt;

&lt;p&gt;In a WebView, the largest contentful element can be an image. If that image is embedded as a data URL, the value can become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data:image/png;base64,&amp;lt;a very long string&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our injected WebView script collected that value and forwarded it to React Native. React Native then attached it as a Sentry span attribute.&lt;/p&gt;

&lt;p&gt;That was the mistake.&lt;/p&gt;

&lt;p&gt;We did not actually need the image bytes. We only wanted to measure page performance by page title, host, path, metric type, and duration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;We removed &lt;code&gt;lcpUrl&lt;/code&gt; and &lt;code&gt;lcpElement&lt;/code&gt; from the Sentry span attributes.&lt;/p&gt;

&lt;p&gt;Before, our LCP payload included extra diagnostic details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;reportWebSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LCP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lcpValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lcpElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;lcpUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the fix, we only report the metric value and stable dimensions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;reportWebSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LCP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lcpValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Sentry attributes now stay small and useful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metricInfo
pageTitle
pageUrl
host
path
durationMs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After removing the oversized fields, the LCP data appeared correctly in the query again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I should have caught earlier
&lt;/h2&gt;

&lt;p&gt;This bug came from a well-intentioned bit of instrumentation code. It tried to capture more context for LCP debugging. That sounds reasonable when you are writing the code.&lt;/p&gt;

&lt;p&gt;But observability data has different rules from application data.&lt;/p&gt;

&lt;p&gt;A field is not harmless just because it is technically available. Before sending it to a telemetry system, it should pass a few checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is it bounded in size?&lt;/li&gt;
&lt;li&gt;Is it low-cardinality enough to query and aggregate?&lt;/li&gt;
&lt;li&gt;Does it help answer a real production question?&lt;/li&gt;
&lt;li&gt;Could it contain private or user-specific data?&lt;/li&gt;
&lt;li&gt;Could one bad value make the rest of the event harder to index or search?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;lcpUrl&lt;/code&gt; failed those checks.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lcpElement&lt;/code&gt; was also not needed for our dashboard. It added noisy DOM details without improving the metrics we actually used.&lt;/p&gt;

&lt;p&gt;One more small trap: our performance transactions were sampled. With &lt;code&gt;tracesSampleRate: 0.2&lt;/code&gt;, historical examples were naturally sparse. That made the bug feel more mysterious than it was.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons learned
&lt;/h2&gt;

&lt;p&gt;For custom performance instrumentation, especially in WebViews, keep span attributes boring.&lt;/p&gt;

&lt;p&gt;Good attributes are small, stable, and useful for grouping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;metricInfo = LCP
pageTitle = Online Customer Support
host = example.com
path = /customer-service
durationMs = 3020
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bad attributes are large, unique, or accidentally full of payload data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lcpUrl = data:image/png;base64,...
outerHTML = &amp;lt;huge DOM subtree&amp;gt;
requestBody = ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My final notes from this one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;entry.url&lt;/code&gt; is not equally useful across WebViews. Android Chromium may give you a short, elided value, while iOS Safari WebView may give you the full data URI.&lt;/li&gt;
&lt;li&gt;Sentry span attributes should not contain potentially huge strings.&lt;/li&gt;
&lt;li&gt;A platform that silently truncates a dangerous value can hide a bug in your instrumentation.&lt;/li&gt;
&lt;li&gt;Spec-like behavior is not always operationally safe. Returning the full URL may be correct, but sending it to telemetry was still wrong.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The dashboard was not wrong. The data model was wrong.&lt;/p&gt;

&lt;p&gt;And in this case, deleting two fields made the metric visible again.&lt;/p&gt;

</description>
      <category>observability</category>
      <category>sentry</category>
      <category>reactnative</category>
      <category>webperf</category>
    </item>
  </channel>
</rss>
