Skip to content

fix(canvas): never leave a canvas blank when its sandbox fails to load - #3975

Draft
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/fix-blank-canvas-sandbox-load-failure
Draft

fix(canvas): never leave a canvas blank when its sandbox fails to load#3975
posthog[bot] wants to merge 1 commit into
mainfrom
posthog-code/fix-blank-canvas-sandbox-load-failure

Conversation

@posthog

@posthog posthog Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Problem

Canvases could load as a completely blank screen — no message, no spinner, nothing to click — and relaunching the app would sometimes bring them back. Raised in #team-canvas and reproduced by two people, on the billing Canvas and then on a second one.

Root cause: a canvas renders inside a sandboxed iframe whose bootstrap statically imported the transpiler from the CDN. If that fetch failed, the whole bootstrap module was discarded — and with it the sandbox's error handlers and its ready handshake. The host therefore received no message at all, and had no way to tell "the runtime never loaded" from "this canvas hasn't rendered yet", so it just showed an empty frame forever. In view mode there is no toolbar, so there was not even an error chip. Recreating the document was the only escape, which is why relaunching appeared to fix it.

Reproduced by loading the real sandbox document with the CDN host unreachable: the iframe's load event fires, #root stays empty, and the host receives zero messages.

Changes

  • Sandbox reports its failures. The transpiler and React are now loaded lazily, and the error reporting is registered before anything that can fail — so a CDN/network failure is reported to the host instead of silently taking the bootstrap down.
  • Boot watchdog. If the sandbox neither renders nor errors within a bounded window (a hung request, a blocked document), the host synthesises an error rather than waiting forever.
  • A recoverable surface instead of a blank one. Canvases now track whether they ever rendered; when they haven't and an error is reported, the view shows an error state with Try again (and Ask agent to fix in edit mode) instead of an empty viewport. A canvas that did render keeps showing, with only the existing toolbar notice — a late data failure doesn't blank a working canvas. "Try again" recreates the frame, which is what actually recovers: a failed module fetch is cached in the document's module map, so re-requesting inside the same document can't succeed.
  • Thumbnails too. A preview that never painted now says "Preview unavailable" instead of looking like an empty canvas.

How did you test this?

  • Drove the real sandbox document in a headless browser with the CDN host blocked:
    • before: iframe loads, #root empty, host receives [] — a blank surface;
    • after: host receives ["ready", {type: "error", …}] and renders the error state;
    • re-posting init in the same document still fails (module map is poisoned), while recreating the frame yields ["ready", "rendered"] and the canvas paints — confirming Try again must, and does, recreate the frame.
  • Screenshotted both states and clicked through Try again → recovered canvas, end to end, with the real CanvasErrorState component. Screenshots are attached to the originating thread.
  • pnpm --filter @posthog/ui test — 2392 passed, including new tests for the sandbox bootstrap ordering, the boot watchdog, the render-health store transitions, and the error component.
  • pnpm --filter @posthog/ui typecheck, pnpm lint clean.

Not addressed here: the separate report of a canvas's data refresh failing after it renders. That path already surfaces an error notice rather than a blank screen, so it needs its own investigation.

Automatic notifications

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

Created with PostHog Desktop from this inbox report.

A canvas renders inside a sandboxed iframe whose bootstrap statically imported
the transpiler from the CDN. When that fetch failed, the whole bootstrap module
was discarded — taking the error handlers and the "ready" handshake with it — so
the sandbox posted nothing at all and the host had no way to tell a failed boot
apart from a canvas that simply had not rendered. The result was a permanently
blank surface: no message, no retry, and in view mode (no toolbar) no affordance
whatsoever. A fresh document was the only way out, which is why relaunching the
app appeared to fix it.

- Load the transpiler and React lazily and register the sandbox's error
  reporting before anything that can fail, so a CDN failure is reported instead
  of silently killing the bootstrap.
- Add a boot watchdog on the host so a boot that stalls without erroring is
  bounded rather than blank forever.
- Track whether a canvas has ever rendered, and when it has not, show a
  recoverable error state (Try again / Ask agent to fix) instead of an empty
  viewport. "Try again" recreates the frame, which is what actually recovers —
  a failed module fetch is cached in the document's module map.
- Give canvas thumbnails the same treatment, so a preview that never painted
  says so instead of looking like an empty canvas.


Generated-By: PostHog Code
Task-Id: a741aee3-3442-431b-b984-28bc75a3b824
@trunk-io

trunk-io Bot commented Jul 29, 2026

Copy link
Copy Markdown

Merging to main 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

Copy link
Copy Markdown

React Doctor found no issues in the changed files. 🎉

Reviewed by React Doctor for commit 53a51dc.

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