From 8d3ff15627ab4f0b84c3019dabbd1f6fb8a8e507 Mon Sep 17 00:00:00 2001 From: "posthog[bot]" <206114724+posthog[bot]@users.noreply.github.com> Date: Sun, 2 Aug 2026 10:09:59 +0000 Subject: [PATCH] fix(error-tracking): widen the view recordings date filter The "View recordings" menu item and button on the error issue page built a replay date filter pinned to [first_seen, last_seen]. For a single-occurrence issue those are the same instant, giving a zero-width window and an empty recordings list. Even for multi-occurrence issues, recordings match on session start time, so a session that began before the first exception fired fell outside the window too. Add `getIssueReplayDateRange` to pad both ends by an hour, matching the padding pattern already used elsewhere in this scene (`getNarrowDateRange`), and use it at both call sites so they can't drift apart again. Generated-By: PostHog Code Task-Id: b0c6f366-72f9-4a60-9bc2-9181ca25807d --- .../ErrorTrackingIssueScene.tsx | 7 +++-- .../error_tracking/frontend/utils.test.ts | 27 ++++++++++++++++++- products/error_tracking/frontend/utils.ts | 11 ++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/products/error_tracking/frontend/scenes/ErrorTrackingIssueScene/ErrorTrackingIssueScene.tsx b/products/error_tracking/frontend/scenes/ErrorTrackingIssueScene/ErrorTrackingIssueScene.tsx index e84a095e5551..d668b5153ad8 100644 --- a/products/error_tracking/frontend/scenes/ErrorTrackingIssueScene/ErrorTrackingIssueScene.tsx +++ b/products/error_tracking/frontend/scenes/ErrorTrackingIssueScene/ErrorTrackingIssueScene.tsx @@ -51,6 +51,7 @@ import { IssueStatusButton } from '../../components/IssueStatusButton' import { ErrorTrackingSetupPrompt } from '../../components/SetupPrompt/SetupPrompt' import { StyleVariables } from '../../components/StyleVariables' import { useErrorTagRenderer } from '../../hooks/use-error-tag-renderer' +import { getIssueReplayDateRange } from '../../utils' import { ErrorTrackingIssueSceneCategory, errorTrackingIssueSceneConfigurationLogic, @@ -124,8 +125,7 @@ export function ErrorTrackingIssueScene(): JSX.Element { { const url = urls.replay(ReplayTabs.Home, { - date_from: issue.first_seen ?? '-30d', - date_to: lastSeen ? lastSeen.toISOString() : null, + ...getIssueReplayDateRange(issue.first_seen, lastSeen), filter_group: { type: FilterLogicalOperator.And, values: [ @@ -176,8 +176,7 @@ export function ErrorTrackingIssueScene(): JSX.Element { /> { }) }) +describe('getIssueReplayDateRange', () => { + it('pads a single-occurrence issue so the window is not zero width', () => { + const seenAt = '2024-01-01T12:00:00.000Z' + const range = getIssueReplayDateRange(seenAt, dayjs(seenAt)) + expect(range.date_from).toEqual('2024-01-01T11:00:00.000Z') + expect(range.date_to).toEqual('2024-01-01T13:00:00.000Z') + }) + + it('extends date_from before first_seen to catch sessions that started earlier', () => { + const firstSeen = '2024-01-01T12:00:00.000Z' + const lastSeen = dayjs('2024-01-02T12:00:00.000Z') + const range = getIssueReplayDateRange(firstSeen, lastSeen) + expect(range.date_from).toEqual('2024-01-01T11:00:00.000Z') + expect(range.date_to).toEqual('2024-01-02T13:00:00.000Z') + }) + + it('falls back to first_seen when last_seen is missing or predates it', () => { + const firstSeen = '2024-01-01T12:00:00.000Z' + expect(getIssueReplayDateRange(firstSeen, null).date_to).toEqual('2024-01-01T13:00:00.000Z') + expect(getIssueReplayDateRange(firstSeen, dayjs('2023-12-31T00:00:00.000Z')).date_to).toEqual( + '2024-01-01T13:00:00.000Z' + ) + }) +}) + describe('sourceDisplay', () => { it('nicely formats paths', async () => { expect(sourceDisplay('')).toEqual('') diff --git a/products/error_tracking/frontend/utils.ts b/products/error_tracking/frontend/utils.ts index 2d3d5680ca42..11185686842c 100644 --- a/products/error_tracking/frontend/utils.ts +++ b/products/error_tracking/frontend/utils.ts @@ -104,6 +104,17 @@ export function isThirdPartyScriptError(value: ErrorTrackingException['value']): return value === THIRD_PARTY_SCRIPT_ERROR } +// Recordings match on session start time, so pad past first_seen to catch a session that began +// before the exception fired, and past last_seen so a single-occurrence issue isn't a zero-width window. +export function getIssueReplayDateRange(firstSeen: string, lastSeen: Dayjs | null): DateRange { + const from = dayjs(firstSeen) + const to = lastSeen && lastSeen.isAfter(from) ? lastSeen : from + return { + date_from: from.subtract(1, 'hour').toISOString(), + date_to: to.add(1, 'hour').toISOString(), + } +} + const customOptions: Record = { dStart: 'Today', // today mStart: 'Month',