Skip to content

Conversation

@simeonlee
Copy link
Member

@simeonlee simeonlee commented Jan 9, 2026

Status: WIP - Do not review yet

This is PR 2 in the error boundaries stack. Stacked on #5588.

The goal of this PR is to show as much of the UI as possible even when there are errors to offer an enhanced dx and ux for first time developers and users - first 30 seconds of product experience is most important.

BEFORE
Screenshot 2026-01-09 at 11 06 12 AM

AFTER
Screenshot 2026-01-09 at 11 22 29 AM

Summary

Enable graceful degradation when infrastructure is unavailable:

  • Root loader catches infra errors (gateway, auth, ClickHouse)
  • Returns config: undefined instead of throwing - could be contentious
  • App shows dismissible ErrorDialog for infra errors
  • Users can browse UI with degraded functionality

Config Optional Changes

  • useConfig() now returns UiConfig | undefined
  • All ~48 consumers updated to handle config?.
  • Components show disabled/empty states when config unavailable
  • Delete PostgresRequiredState (replaced by error system)

Test plan

  • Gateway down: Shows dismissible dialog, sidebar works
  • ClickHouse down: Shows dismissible dialog, sidebar works
  • Auth failed: Shows dismissible dialog
  • Components handle undefined config gracefully

PR Stack

Base automatically changed from simeonlee/error-boundaries-2-layout-boundaries to simeonlee/error-boundaries-1-root-errors January 9, 2026 16:17
@simeonlee simeonlee changed the title WIP: Error boundaries PR 3/3 - Graceful degradation WIP: Error boundaries: Graceful degradation and optimistic UI Jan 9, 2026
@simeonlee simeonlee force-pushed the simeonlee/error-boundaries-3-graceful-degradation branch 2 times, most recently from bfc267f to 15c6546 Compare January 9, 2026 16:56
@simeonlee simeonlee force-pushed the simeonlee/error-boundaries-3-graceful-degradation branch from 1bf2460 to afdaaf9 Compare January 9, 2026 19:14
Enable graceful degradation when gateway or ClickHouse is unavailable:
- Root loader catches infra errors, returns config: undefined
- App shows dismissible ErrorDialog for infra errors
- ConfigProvider now accepts undefined
- useConfig() returns UiConfig | undefined
- All consumers updated to handle config?.
- Components show disabled/empty states when config unavailable
- Delete PostgresRequiredState (replaced by error system)

This allows users to browse the UI (with degraded functionality)
even when infrastructure has issues.
- Update LaunchEvaluationModal to handle optional config
- Update FunctionSelector to use getPrefix instead of getItemIcon
- Add null coalescing for functions and config at call sites
- Fix 404 handling to use AppProviders instead of RootErrorBoundaryLayout
- Revert models/route.tsx to use local QUANTILES constant
…ding

Based on React Router 7 official patterns and industry best practices:

1. **Use undefined over null for unavailable data**
   - Changed useAllFunctionConfigs() to return undefined instead of null
   - Updated FunctionSelector to handle undefined gracefully with disabled state

2. **Remove scattered null coercion (??  {})**
   - Removed all `functions ?? {}` patterns across components
   - Let components properly handle undefined state

3. **Remove anti-pattern: catching errors to return fake empty data**
   - Removed loader try/catch blocks that returned empty arrays/objects
   - Let infrastructure errors throw to layout error boundaries naturally
   - Removed fake degraded states that hid real errors from users

4. **Let errors flow to proper boundaries**
   - Layout error boundaries handle infrastructure failures
   - Dashboard uses <Await errorElement> for granular per-metric errors
   - Pages with promises use React.use() which throws to boundaries

Research sources:
- https://reactrouter.com/how-to/error-boundary
- https://reactrouter.com/en/main/components/await
- https://reactrouter.com/6.30.2/guides/deferred

Routes updated:
- workflow_evaluations/route.tsx
- evaluations/route.tsx
- datasets/route.tsx
- observability/episodes/route.tsx
- index.tsx (dashboard)
Replace use() hook with Await component pattern across all data-loading
components for inline error handling. This allows errors to be displayed
at the lowest possible UI level - tables show "Error loading data" while
page headers and navigation remain functional.

Components updated:
- InferencesTable: Shows inline error instead of blank page
- EpisodesTable: Shows inline error with message extraction
- EpisodeInferenceTable: Shows inline error in episode detail
- DatasetTable: Shows inline error in datasets list
- DatasetRowTable: Shows inline error for datapoints
- AutopilotSessionsTable: Shows inline error for sessions
- PageLayout counts: Shows "—" with tooltip on count load failure
- Episode detail pagination/feedback sections

Also:
- Remove fake data catches from inferences loader (.catch(() => 0))
- Fix BigInt comparison bug (numInferences === 0n vs Number())
- Distinguish client 404s (!error.data) from resource 404s
- Defer modelInferences and feedback data as promises in loader
- Add skeleton and error components for streamed sections
- Update BasicInfo to support both promise (server) and resolved (client) data
- Replace use() with Await in autopilot session for consistent error handling
- Fix bigint to number conversion for processing_time_ms
…page

Port the full variant response logic from InferenceDetailContent to restore
TryWithButton functionality that was broken during streaming refactor.

- Add variant inference fetcher with proper request handling
- Add VariantResponseModal with demonstration feedback support
- Track resolved model inferences for usage calculation in modal
- Use useCallback for handleFeedbackAdded to satisfy exhaustive-deps
@simeonlee simeonlee force-pushed the simeonlee/error-boundaries-3-graceful-degradation branch from 9540d9d to a375279 Compare January 9, 2026 21:07
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.

2 participants