Skip to content

Commit c98532d

Browse files
logaretmclaude
andcommitted
fix(browser): treat 0 as an implausible LCP value
LCP is a paint time — 0ms is physically impossible. Accepting it let the uninitialized default (0) leak through as a spurious span when the only observed LCP value was filtered out as implausibly high. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 128ab47 commit c98532d

File tree

3 files changed

+12
-3
lines changed

3 files changed

+12
-3
lines changed

packages/browser-utils/src/metrics/lcp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { listenForWebVitalReportEvents, msToSec, startStandaloneWebVitalSpan, su
2121
export const MAX_PLAUSIBLE_LCP_DURATION = 60_000;
2222

2323
export function isValidLcpMetric(lcpValue: number | undefined): lcpValue is number {
24-
return lcpValue != null && lcpValue >= 0 && lcpValue <= MAX_PLAUSIBLE_LCP_DURATION;
24+
return lcpValue != null && lcpValue > 0 && lcpValue <= MAX_PLAUSIBLE_LCP_DURATION;
2525
}
2626

2727
/**

packages/browser-utils/test/metrics/lcp.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ vi.mock('@sentry/core', async () => {
1515

1616
describe('isValidLcpMetric', () => {
1717
it('returns true for plausible lcp values', () => {
18-
expect(isValidLcpMetric(0)).toBe(true);
18+
expect(isValidLcpMetric(1)).toBe(true);
1919
expect(isValidLcpMetric(2_500)).toBe(true);
2020
expect(isValidLcpMetric(MAX_PLAUSIBLE_LCP_DURATION)).toBe(true);
2121
});
2222

2323
it('returns false for implausible lcp values', () => {
2424
expect(isValidLcpMetric(undefined)).toBe(false);
25+
expect(isValidLcpMetric(0)).toBe(false);
2526
expect(isValidLcpMetric(-1)).toBe(false);
2627
expect(isValidLcpMetric(MAX_PLAUSIBLE_LCP_DURATION + 1)).toBe(false);
2728
});

packages/browser-utils/test/metrics/webVitalSpans.test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as SentryCore from '@sentry/core';
22
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
33
import * as inpModule from '../../src/metrics/inp';
4+
import { MAX_PLAUSIBLE_LCP_DURATION } from '../../src/metrics/lcp';
45
import { _emitWebVitalSpan, _sendClsSpan, _sendInpSpan, _sendLcpSpan } from '../../src/metrics/webVitalSpans';
56

67
vi.mock('@sentry/core', async () => {
@@ -262,7 +263,7 @@ describe('_sendLcpSpan', () => {
262263
});
263264

264265
it('sends a streamed LCP span without entry data', () => {
265-
_sendLcpSpan(0, undefined);
266+
_sendLcpSpan(250, undefined);
266267

267268
expect(SentryCore.startInactiveSpan).toHaveBeenCalledWith(
268269
expect.objectContaining({
@@ -271,6 +272,13 @@ describe('_sendLcpSpan', () => {
271272
}),
272273
);
273274
});
275+
276+
it('drops implausible LCP values', () => {
277+
_sendLcpSpan(0, undefined);
278+
_sendLcpSpan(MAX_PLAUSIBLE_LCP_DURATION + 1, undefined);
279+
280+
expect(SentryCore.startInactiveSpan).not.toHaveBeenCalled();
281+
});
274282
});
275283

276284
describe('_sendClsSpan', () => {

0 commit comments

Comments
 (0)