Skip to content

fix(replay): stop mid-load unmounts breaking embedded recordings lists - #81597

Open
mp-hog wants to merge 3 commits into
masterfrom
posthog/fix-embedded-playlist-unmount-crash
Open

fix(replay): stop mid-load unmounts breaking embedded recordings lists#81597
mp-hog wants to merge 3 commits into
masterfrom
posthog/fix-embedded-playlist-unmount-crash

Conversation

@mp-hog

@mp-hog mp-hog commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Problem

Leaving an embedded recordings list while it is still loading throws a KEA store error, and later visits can find the list stuck on "Loading recordings…". The experiment Recordings tab hits this hardest: users open the tab, click away mid-load, and it comes back blank.

  • loadSessionRecordings reads values.filters after await api.recordings.list(...) resumes, to report fetch analytics.
  • Reading values on an unmounted keyed logic throws [KEA] Can not find path "…" in the store, which kea-loaders turns into a spurious loadSessionRecordingsFailure.
  • The listener's breakpoint() — the bail-out kea arms on unmount — sits below that read, so it never gets to stop the listener.

Error tracking shows keyed variants of this daily, rising since the experiment-recordings-tab rollout put more embedded lists in front of users: experiment tabs and bare keys, vision scanner runs.

Complements #78247, which guards component-side bare reads of this logic. This PR guards the loader's own async resume.

Changes

  • loadSessionRecordings captures values.filters once, before any await, and reports fetch analytics from the captured value. Actions are safe to dispatch after unmount; only values reads throw.
  • The existing breakpoint() stays after the report, still guarding the post-await values reads in the return value.
  • Alternative rejected: moving breakpoint() above the report. That drops superseded and abandoned fetches — the slow tail — from replay_list_load_ms, skewing the latency histogram toward fast responses.
  • Side fix: a superseded load used to report the new filters with the old request's timing. It now reports the filters the request was built from.

How did you test this code?

  • Added two jest regression tests, each verified to fail against the pre-branch code:
    • Unmount mid-flight: holds api.recordings.list on a controlled promise, unmounts, resolves. Fails before (KEA store error → failure action); passes now.
    • Supersede mid-flight: changes filters while a load is in flight, then resolves the stale response. Locks in that the fetch report still fires with request-time filters; also fails if breakpoint() moves above the report.
  • pnpm --filter=@posthog/frontend fix — clean.
  • Local typescript:check reports only pre-existing errors from the unbuilt local @posthog/quill workspace, none in changed files; CI is the authority.
  • Not done: no manual browser reproduction of the race.

Automatic notifications

  • Publish to changelog?

Docs update

None.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • Takes over the original breakpoint-move fix on this branch after review flagged its side effect — superseded fetches vanishing from analytics — as avoidable.
  • Verified against kea 4 pre-release source: beforeUnmount bumps every listener breakpoint counter, kea-loaders swallows breakpoint throws, and built-logic action dispatches have no mount check.
  • Cross-checked in-repo precedents: databaseTableListLogic (isMounted() guards), dataNodeLogic (AbortController + breakpoints).
  • Tools: Claude Code (Fable 5) via PostHog Desktop. Skills: /writing-tests, /writing-code-comments, /writing-pr-descriptions.

Created with PostHog Desktop

loadSessionRecordings read values.filters after the API await resumed but before its breakpoint() guard, so embedded playlists (experiment recordings tab, vision scanner runs) that unmounted mid-request threw "[KEA] Can not find path" and dispatched a spurious failure. Move the breakpoint above the values reads; kea bumps breakpoint counters on beforeUnmount, so the resumed listener now aborts silently.

Generated-By: PostHog Desktop
Task-Id: 03c64c82-7dfc-4d5f-8fed-3d5de52245f5
@trunk-io

trunk-io Bot commented Aug 11, 2026

Copy link
Copy Markdown

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

Bundle size — 🟢 -2.1 KiB (-0.0%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 67.75 MiB · 🟢 -2.1 KiB (-0.0%)

File Size Δ vs base
exporter/src/exporter/scenes/ExporterDashboardScene.js 4.0 KiB 🟢 -286.1 KiB (-98.6%)
exporter/src/exporter/scenes/ExporterNotebookScene.js 3.31 MiB 🔺 +243.4 KiB (+7.7%)
posthog-app/_parent/products/workflows/frontend/Workflows/WorkflowScene.js 91.9 KiB 🟢 -102.5 KiB (-52.7%)
exporter/_parent/products/workflows/frontend/Workflows/Workflow.js 95.1 KiB 🔺 +95.1 KiB (new)
posthog-app/_parent/products/error_tracking/frontend/scenes/ErrorTrackingIssueScene/ErrorTrackingIssueScene.js 54.3 KiB 🟢 -48.9 KiB (-47.4%)
posthog-app/_parent/products/actions/frontend/pages/Action.js 9.2 KiB 🟢 -22.2 KiB (-70.7%)
posthog-app/src/scenes/authentication/signup/Signup.js 19.8 KiB 🔺 +19.8 KiB (new)
posthog-app/_parent/products/signals/frontend/inbox/InboxScene.js 721.0 KiB 🔺 +18.1 KiB (+2.6%)
render-query/src/render-query/render-query.js 21.74 MiB 🔺 +16.9 KiB (+0.1%)
posthog-app/src/scenes/authentication/invite-signup/InviteSignup.js 16.3 KiB 🔺 +14.6 KiB (+825.9%)
posthog-app/src/scenes/authentication/login/Login.js 9.4 KiB 🔺 +7.5 KiB (+405.6%)
posthog-app/src/scenes/authentication/verify-email/VerifyEmail.js 6.4 KiB 🔺 +4.6 KiB (+261.5%)
posthog-app/_parent/products/workflows/frontend/Workflows/Workflow.js 4.3 KiB 🔺 +4.3 KiB (new)
exporter/_parent/products/customer_analytics/frontend/components/AccountRelationshipsInput/CyclotronJobInputAccountRelationships.js 3.6 KiB 🔺 +3.6 KiB (new)
exporter/_parent/products/customer_analytics/frontend/components/AccountPropertiesInput/CyclotronJobInputAccountProperties.js 3.3 KiB 🔺 +3.3 KiB (new)
posthog-app/_parent/products/logs/frontend/scenes/LogsAlertDetailScene/LogsAlertDetailScene.js 29.5 KiB 🔺 +3.3 KiB (+12.4%)
exporter/_parent/products/alerts/frontend/views/EditAlertModal.js 114.6 KiB 🟢 -3.1 KiB (-2.6%)
posthog-app/_parent/products/logs/frontend/LogsScene.js 37.6 KiB 🔺 +2.8 KiB (+8.0%)
exporter/_parent/products/conversations/frontend/components/SlaBusinessHours/CyclotronJobInputBusinessHours.js 2.7 KiB 🔺 +2.7 KiB (new)
posthog-app/src/scenes/authentication/signup/SignupContainer.js removed 🟢 -1.8 KiB (-100.0%)
exporter/src/exporter/exporter.js 17.4 KiB 🟢 -1.7 KiB (-9.0%)
posthog-app/src/scenes/notebooks/NotebookCanvasScene.js 16.9 KiB 🔺 +1.6 KiB (+10.2%)
posthog-app/src/scenes/notebooks/NotebookPanel/NotebookPanel.js 18.6 KiB 🔺 +1.6 KiB (+9.2%)
posthog-app/src/scenes/notebooks/NotebookScene.js 24.5 KiB 🔺 +1.6 KiB (+6.8%)
posthog-app/src/scenes/persons/PersonScene.js 36.9 KiB 🔺 +1.4 KiB (+3.9%)
exporter/_parent/products/conversations/frontend/components/Assignee/CyclotronJobInputAssignee.js 1.3 KiB 🔺 +1.3 KiB (new)
exporter/_parent/products/conversations/frontend/components/TicketTags/CyclotronJobInputTicketTags.js 1.1 KiB 🔺 +1.1 KiB (new)

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1.27 MiB · 22 files 🔺 +3.6 KiB (+0.3%) ███░░░░░░░ 28.1% of 4.51 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.47 MiB · 3,134 files 🔺 +115.8 KiB (+1.4%) █████████░ 87.2% of 9.71 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
789 B src/scenes/ChunkLoadErrorBoundary.tsx
762 B src/index.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
285.3 KiB ../node_modules/.pnpm/posthog-js@1.410.1/node_modules/posthog-js/dist/rrweb.js
267.7 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
238.4 KiB src/taxonomy/core-filter-definitions-by-group.json
231.5 KiB ../node_modules/.pnpm/posthog-js@1.410.1/node_modules/posthog-js/dist/module.js
154.3 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
105.2 KiB src/lib/api.ts
94.6 KiB ../packages/quill/packages/quill/dist/index.js
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

Toolbar bundle — eager 2.20 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.20 MiB · 17 files 🔺 +3.8 KiB (+0.2%) ████░░░░░░ 38.5% of 5.72 MiB
Deferred (lazy) 2.08 MiB · 33 files 🟢 -15 B (-0.0%) n/a — loads on demand
Loader dist/toolbar.js 1.1 KiB no change █░░░░░░░░░ 5.8% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
729.2 KiB dist/toolbar/toolbar-app-KABLUAZC.css
553.0 KiB dist/toolbar/chunk-chunk-CAJJBQHG.js
484.6 KiB dist/toolbar/chunk-chunk-UCWKK2W2.js
133.6 KiB dist/toolbar/chunk-chunk-RRH6FYIR.js
131.8 KiB dist/toolbar/chunk-chunk-T5KY5WYR.js
71.2 KiB dist/toolbar/toolbar-app-XRBEOWR6.js
69.0 KiB dist/toolbar/chunk-chunk-27JL52RE.js
35.6 KiB dist/toolbar/chunk-chunk-YBL2LK2H.js
20.9 KiB dist/toolbar/chunk-chunk-QGUDWYKZ.js
12.2 KiB dist/toolbar/chunk-chunk-PIK3PADE.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

Dist folder size — 🔺 +7.55 MiB (+0.5%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1429.02 MiB · 🔺 +7.55 MiB (+0.5%)

The `as ReturnType<typeof api.recordings.list>` cast contextually typed the Promise constructor, so its resolve callback no longer matched the `(value: unknown) => void` holder under strict function types (TS2322, caught by CI's Frontend typechecking). Build the promise untyped and cast only at the mock's return position.

Generated-By: PostHog Desktop
Task-Id: 03c64c82-7dfc-4d5f-8fed-3d5de52245f5
@mp-hog mp-hog added the run-ci-frontend Force ci-storybook's full visual-regression matrix to run even on a draft PR label Aug 12, 2026 — with PostHog
@mp-hog mp-hog closed this Aug 12, 2026
@mp-hog mp-hog reopened this Aug 12, 2026
@trunk-io

trunk-io Bot commented Aug 12, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

Capture values.filters before the awaits and report the fetch from the
captured value, restoring breakpoint() to after the report.

Superseded and abandoned fetches keep counting toward the list load-time
metrics (no survivorship bias toward fast responses), and the report now
carries the filters the request was built from rather than whatever they
are when the response lands. Unmount safety is unchanged: the only
post-await `values` reads sit behind the breakpoint, and dispatching an
action on an unmounted logic is safe — only `values` reads throw.

Adds a regression test that fails both if the report goes back to
reading live values and if breakpoint() moves back above the report.

Generated-By: PostHog Desktop
Task-Id: 139ec0b8-42da-4ec0-b68d-d0a0e238992e
@mp-hog mp-hog changed the title fix(replay): abort in-flight list load when playlist logic unmounts fix(replay): stop mid-load unmounts breaking embedded recordings lists Aug 12, 2026
@mp-hog
mp-hog marked this pull request as ready for review August 12, 2026 20:33
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

🦔 Hogbox preview · ✅ ready

▶ Open the preview

🔑 Login test@posthog.com / 12345678 (demo data)
🧩 Running this PR's backend and frontend, on the PostHog :master base
🔗 Link stable across rebuilds — a re-push swaps the box underneath, the URL stays
🔒 Access tailnet only (PostHog VPN)
🛠️ Admin inspect & debug state in hogland
💤 Idle sleeps after ~30 min idle (snapshot to S3, zero node cost) and wakes on your next visit in ~30s, behind a brief "waking up" screen

commit 17686d4 · box box-6b2475559f49 · ready in 654s (push → usable) · build log · rebuilds on every push, torn down on close

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested review from a team, TueHaulund, arnohillen, fasyy612 and ksvat and removed request for a team August 12, 2026 20:33
@greptile-apps

greptile-apps Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "fix(replay): keep fetch reporting for su..." | Re-trigger Greptile

@mp-hog mp-hog added stamphog Request AI approval (no full review) skip-inkeep-docs Use this label to skip an Inkeep docs PR in posthog.com labels Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-ci-frontend Force ci-storybook's full visual-regression matrix to run even on a draft PR skip-inkeep-docs Use this label to skip an Inkeep docs PR in posthog.com stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant