PostHog / PostHog/posthog

Canvas data requests over the 8-request cap are rejected instead of queued, and are never retried

Open
#103,199 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

backlog bug feature/desktop
Dominant language
Python
Stars
39.9k
Forks
3.4k
Avg merge
6h 51m
Merged PRs (30d)
232

Description

Bug Description

A canvas that starts more than 8 data requests at once shows "Couldn't load: Canvas data request exceeds runtime limits" on the cards that lose the race. The host does not queue the extra requests, and the SDK does not retry them, so those cards stay failed until the whole canvas is refreshed.

What the code does today

The cap rejects, it does not queue. canvasHostMessageRouter.ts#L124 is a counter check. If activeDataRequests >= MAX_CONCURRENT_DATA_REQUESTS (8), the host posts ok: false and drops the message. The slot is released in the finally block, but nothing is re-driven, because the rejected request was never kept.

The cap exists as a security guard — the comment above the constant says a runaway loop must not pile up unbounded concurrent requests. A bounded FIFO queue meets that goal too, so rejection is not required by the guard.

There is no retry. The call() bridge in sandboxRuntime.ts#L222 posts once and rejects on the first failure. There is no attempt counter, no backoff, and no connectivity check. A capacity rejection, a 30s timeout, and a dropped connection all surface the same way: one failed card, immediately, with no second attempt.

The error string is ambiguous. The same message is returned for three different causes: the data-request cap, the separate connector-request cap, and a payload over the 64 KB limit. A reader cannot tell which one they hit.

Why it matters

A canvas author has no way to make this reliable from inside the canvas. Guidance today is to self-throttle, which puts a platform concern in every canvas's source, and every author has to solve it again. From the viewer's side the failure looks random, because which cards fail depends on request timing rather than on anything about the canvas.

The user expectation, as raised, is the ordinary one: show loading, retry a bounded number of times, and stop on success, on exhausted attempts, or when the client is offline — and queue work over the cap rather than discard it.

Possible directions (for the team to choose)
  1. Bounded FIFO queue in the host router — admit up to the cap, queue the rest to a fixed depth, and reject only past that depth.

  2. Retry with backoff in the SDK call path — a capped number of attempts, keep the card in its loading state while retrying, stop on success, exhaustion, or loss of connectivity.

  3. Split the error string so the three causes are distinguishable.

  4. Emit an event when the host rejects a data request. There is currently no telemetry for these rejections, so the failure cannot be counted across canvases. The existing canvas runtime error event does not record them.

  5. Give the failure state a proper design, with a retry button that works. Quill's blocks layer, which would own a shared EmptyState / ErrorState pattern, is currently an empty module (packages/quill/packages/blocks/src/index.ts), so every canvas hand-rolls its own failure card. In practice that means a line of red error text and a small outline button — no icon, no explanation of what failed or what to do, and no visual difference between "this failed" and "there is no data". Shipping one shared block would fix it once for every canvas instead of once per author. Note that per-card retry only becomes meaningful once direction 2 exists; today a canvas author has nothing to wire the button to except re-running the same query that was just rejected.
    1 and 2 are complementary: the queue removes most rejections, and retry covers timeouts and transient network failures. 5 is what the viewer actually sees when the first four do not save them.


Raised in this Slack thread. Filing for team-surfaces to decide the approach.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with products/desktop/packages/ui/src/features/canvas/freeform/canvasHostMessageRouter.ts around line 124 and sandboxRuntime.ts around line 222; inspect how capacity failures and bridge failures are currently handled. The issue lists several possible directions, so confirm the team's chosen scope before changing code. Done is a bounded, testable behavior for queued requests, retries, error reporting, or the shared failure state, as selected by the team.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
desktop, frontend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.