Skip to content

fix(insights): block on cache miss for any dashboard-opened insight - #76386

Draft
posthog[bot] wants to merge 1 commit into
masterfrom
posthog-self-driving/fixinsights-stop-blank-charts-when-32c002
Draft

fix(insights): block on cache miss for any dashboard-opened insight#76386
posthog[bot] wants to merge 1 commit into
masterfrom
posthog-self-driving/fixinsights-stop-blank-charts-when-32c002

Conversation

@posthog

@posthog posthog Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Opening an insight from a dashboard can dead-end on "Chart data didn't load" with no way out except a manual Refresh or renaming the insight to force a reload.
  • #75858 fixed this for loads that carry non-empty dashboard filter/variable overrides, but telemetry (insight refresh hint shown) kept firing at the same 350–800/day, up to 96 distinct users, 100% dashboard-scoped rate through the day after it shipped.
  • urls.insightView builds the dashboard-tile → insight link by filtering override params with Boolean(value) (products.tsx:1287), and Boolean({}) is true — so almost every dashboard tile click sends filters_override={}, variables_override={}, tile_filters_override={} in the URL, even when nothing is actually overridden.
  • insightOverridesPresent correctly treats those empty objects as "no override", which is right for editing/cache-key purposes. But insightLogic.loadInsight used that same flag to decide whether to escalate the refresh mode — so a dashboard-opened insight with no real overrides stayed on plain async. If the insight's own saved-query cache happened to be cold (e.g. it isn't warmed by any dashboard's scheduled refresh yet), async returns result: null and nothing polls or reloads it. The afterMount force-refresh in insightDataLogic can't help either, since it only runs when cachedInsight is passed through, and InsightAsScene only passes it when hasOverrides is true.

Changes

  • insightLogic.loadInsight now escalates to async_except_on_cache_miss whenever the insight was opened with a dashboard id (props.dashboardId != null), not only when overrides are non-empty. Warm and stale keys behave exactly as before; a genuine miss now computes synchronously instead of dead-ending.

How did you test this code?

  • insightLogic.test.ts — added a regression case pinning the refresh param for a dashboard-opened insight with empty overrides (previously async, now async_except_on_cache_miss), and a case confirming empty overrides outside dashboard context still use async. 53/53 tests pass.
  • oxlint/oxfmt via pnpm --filter=@posthog/frontend fix — clean.
  • pnpm --filter=@posthog/frontend typescript:check — no errors on touched files (pre-existing failures elsewhere in the repo, unrelated).
  • Not done: no manual browser verification in this sandbox. To reproduce: open a dashboard tile whose insight's saved-query cache is cold, and confirm the chart renders instead of "Chart data didn't load".

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Docs update

No user-facing docs affected.

🤖 Agent context

Autonomy: Fully autonomous

  • Investigated via an inbox report tracing the residual "Chart data didn't load" telemetry after fix(insights): compute on cache miss when loading an insight with dashboard overrides #75858. Checked for competing open PRs/issues on insightLogic.tsx/insightDataLogic.tsx first — found none in flight.
  • Traced why the empty-override case still breaks: confirmed server-side (posthog/utils.py, posthog/api/services/query.py) that an empty override dict is a cache-key no-op, then found the actual gap is on the frontend refresh-mode decision, not the cache key itself — a dashboard-opened insight has no escalation path at all once hasOverrides is false, regardless of whether its own cache happens to be cold.
  • Considered widening insightOverridesPresent itself, but that flag is also used for editingDisabledReason and the overrides banner, where empty-object overrides correctly mean "editable, no banner". Scoped the fix to the refresh-mode decision in loadInsight instead.
  • Left the second reported failure (InsightErrorState / unattributable query_kind bucket) untouched — it's a genuine server-side query failure, a different code path, and not what this fix addresses.

Created with PostHog Desktop from this inbox report.

Widen the `async_except_on_cache_miss` escalation added in #75858 from "overrides are non-empty" to "opened from a dashboard", since a saved insight's own cache key can be cold too and that path had no other recovery.

Generated-By: PostHog Code
Task-Id: a8e2a4e0-48bd-48cf-8ebc-3956899422dd
@trunk-io

trunk-io Bot commented Aug 2, 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 2, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Bundle size — 🔺 +21 B (+0.0%)

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

Total: 65.51 MiB · 🔺 +21 B (+0.0%)

No file changed by more than 1000 B.

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.25 MiB · 22 files no change ███░░░░░░░ 27.7% of 4.51 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.13 MiB · 3,035 files 🔺 +21 B (+0.0%) ████████░░ 83.8% 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.6 KiB ../node_modules/.pnpm/posthog-js@1.409.5/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
235.5 KiB src/taxonomy/core-filter-definitions-by-group.json
231.5 KiB ../node_modules/.pnpm/posthog-js@1.409.5/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.3 KiB src/lib/api.ts
94.7 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.19 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.19 MiB · 17 files no change ████░░░░░░ 38.3% of 5.72 MiB
Deferred (lazy) 2.08 MiB · 33 files no change 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
718.3 KiB dist/toolbar/toolbar-app-FT4UJ3IP.css
551.4 KiB dist/toolbar/chunk-chunk-AGSGCCBT.js
484.6 KiB dist/toolbar/chunk-chunk-T44C2V5C.js
133.6 KiB dist/toolbar/chunk-chunk-KBLX73CM.js
131.8 KiB dist/toolbar/chunk-chunk-T5KY5WYR.js
71.0 KiB dist/toolbar/toolbar-app-FKUW2FOM.js
69.0 KiB dist/toolbar/chunk-chunk-27JL52RE.js
35.6 KiB dist/toolbar/chunk-chunk-3JVI3ZTF.js
20.9 KiB dist/toolbar/chunk-chunk-HJ3ZJMTU.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 — 🔺 +1.5 KiB (+0.0%)

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

Total: 1381.30 MiB · 🔺 +1.5 KiB (+0.0%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants