Skip to content

fix(dash0): live-socket CONNECTING crash + route-level error boundaries - #108

Merged
fclairamb merged 2 commits into
mainfrom
fix/live-socket-connecting-crash
Jul 6, 2026
Merged

fclairamb merged 2 commits into
mainfrom
fix/live-socket-connecting-crash

Conversation

@fclairamb

Copy link
Copy Markdown
Owner

Problem

Full-app crash: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.

The live-updates socket client (web/dash0/src/lib/live-socket.ts) exposed its send() surface at socket creation time. Any component mounting while the socket was dialing (first connect or any reconnect attempt) made the subscription registry send a subscribe frame into a CONNECTING socket, which throws — inside a React effect, unwinding the entire route tree to the router's bare default error screen.

Fix

  • send() only gets the socket once the server acks auth (hello) — the server handshake requires auth as the first frame anyway, so earlier frames were invalid regardless. Frames before that are dropped, which is safe: onOpen replays every active scope after each (re)connect, and an unsubscribe for a never-subscribed scope needs no undoing server-side.
  • The test FakeSocket now mimics the browser (throws on send while CONNECTING; discards after close), plus three regression tests covering the pre-open, open-but-pre-hello, and mid-reconnect windows.

Better error UX

  • New shared ErrorFallbackCard (friendly copy, collapsible technical details, Try again + Reload page) in error-boundary.tsx.
  • Wired as the router's defaultErrorComponent: a route error now renders inside the layout — sidebar and navigation stay usable — and "Try again" resets the boundary and re-runs loaders. Errors feed the existing bug-report ring buffer.
  • Root ErrorBoundary reuses the card; pattern added to the design reference (Feedback section).

Testing

  • vitest run — 107/107 pass (incl. 3 new regression tests)
  • tsc -b clean; eslint: no new errors (only the known pre-existing Date.now() debt in design-reference)
  • Verified live against the dev server with Playwright: dashboard loads through the previously-crashing window with no console errors; error card renders in the design reference.

🤖 Generated with Claude Code

… boundaries

The live-updates WebSocket client exposed its send() surface as soon as the
socket was created (readyState CONNECTING). A component mounting during that
dial window - or during a reconnect attempt - made the subscription registry
send a subscribe frame immediately, and the browser threw "Failed to execute
'send' on 'WebSocket': Still in CONNECTING state" inside a React effect,
blanking the whole app.

The socket is now handed to send() only once the server acks auth (hello),
matching the server handshake which requires auth as the first frame. Frames
sent before that are dropped, which is safe: onOpen replays every active
scope after each (re)connect, and an unsubscribe for a never-subscribed
scope needs no undoing. The test FakeSocket now mimics the browser (throws
on send while CONNECTING) and three new tests pin the pre-open, pre-hello,
and mid-reconnect windows.

On the UX side, route errors no longer fall through to TanStack Router's
bare default screen: a shared ErrorFallbackCard (friendly copy, collapsible
technical details, Try again + Reload actions) is wired as the router's
defaultErrorComponent, so a crash renders inside the layout with the sidebar
still usable, and "Try again" re-runs loaders after resetting the boundary.
The root ErrorBoundary reuses the same card, and the pattern is catalogued
in the design reference.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@fclairamb
fclairamb enabled auto-merge (squash) July 6, 2026 17:57
@fclairamb
fclairamb merged commit 008c015 into main Jul 6, 2026
11 checks passed
@fclairamb
fclairamb deleted the fix/live-socket-connecting-crash branch July 6, 2026 18:18
@fclairamb fclairamb mentioned this pull request Jul 6, 2026
fclairamb added a commit that referenced this pull request Jul 8, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.2.1](v0.2.0...v0.2.1)
(2026-07-08)


### Features

* **dash0:** one-click MCP connector setup — AI assistants page with a
URL-only OAuth flow, per-client icons, shared copy/collapsible code
primitives ([#111](#111))
([d105be4](d105be4))
* **dash0:** Recent activity links directly to the originating
incident/check and shows the channel name; incident lifecycle events
enriched with check UID/name
([#111](#111))
([d105be4](d105be4))
* **entitlements:** SaaS Free-plan defaults now match the billing Free
plan (10 checks, 6/min, no SSO), with a backend-driven
"Free"/"Team"/"Self-hosted" plan display identity
([#111](#111))
([d105be4](d105be4))
* **devloop:** `make dev`/`make dev-test` supervise dash0 and status0
with size-rotated per-process logs instead of unbounded raw output
([#111](#111))
([d105be4](d105be4))


### Bug Fixes

* **checks:** list endpoint no longer overfetches O(retention) raw rows
per check for `lastResult` — one row per check via `DISTINCT ON`
([#111](#111))
([d105be4](d105be4))
* **e2e:** dashboard E2E suite flaked on per-test login contention;
authenticate once per Playwright worker instead of once per test
([#111](#111))
([d105be4](d105be4))
* **db:** embedded-Postgres orphan sweep + watchdog reclaims leaked
dev/test instances left behind by crashed processes
([#111](#111))
([d105be4](d105be4))
* **realtime:** check-detail live subscription used the URL slug instead
of the canonical uid, silently breaking live updates on slug-based check
URLs ([#111](#111))
([d105be4](d105be4))
* **e2e:** hardcoded `localhost:4000` in several spec files ignored
`E2E_BASE_URL`, breaking test runs against non-default servers
([#111](#111))
([d105be4](d105be4))
* **checkworker:** check results fell back to a null region for
default-region checks instead of the executing worker's own region
([#111](#111))
([d105be4](d105be4))
* **notifications:** a malformed notification UID errored on the
Postgres `uuid` column cast instead of rendering the friendly not-found
state ([#111](#111))
([d105be4](d105be4))
* **dash0:** live-socket CONNECTING crash + route-level error boundaries
([#108](#108))
([008c015](008c015))
* **deps:** update go dependencies (non-major)
([#109](#109))
([ec616d4](ec616d4))
* **deps:** update module github.com/oapi-codegen/oapi-codegen/v2 to
v2.7.2 ([#110](#110))
([09d6ad0](09d6ad0))
* **deps:** update module github.com/ohler55/ojg to v1.28.2
([#107](#107))
([335615c](335615c))
* **deps:** update module github.com/wneessen/go-mail to v0.8.0
([#103](#103))
([aa533c1](aa533c1))
* Slack multi-org, region filtering, mail-checker hang fixes,
live-status dot
([#100](#100))
([35f3aa2](35f3aa2))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
This was referenced Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant