Skip to content

fix(desktop): show an error state instead of a blank window when organization.list fails (#6794) - #6810

Open
Kitenite wants to merge 1 commit into
mainfrom
fix/6794-org-list-error-state
Open

fix(desktop): show an error state instead of a blank window when organization.list fails (#6794)#6810
Kitenite wants to merge 1 commit into
mainfrom
fix/6794-org-list-error-state

Conversation

@Kitenite

@Kitenite Kitenite commented Aug 24, 2026

Copy link
Copy Markdown
Member

Problem

A returning window (one with an org already in the window registry) rendered an empty, undraggable frame when organization.list failed but /api/auth/* still answered. CollectionsProvider refuses to adopt a registry org it can't verify against the membership list (correct), but its render site treated "still loading" and "load failed" identically and returned null, unmounting the whole authenticated tree. tRPC-shaped 500s get zero retries, so an API outage looked like a hang.

Fix

  • resolveWindowOrg.ts: the wait / unresolvable / resolved decision extracted as a pure function. The one-shot initializedRef semantics and the "never adopt an unverified registry org" invariant are unchanged: unresolvable is rendered, never adopted.
  • components/OrgResolutionScreen: replaces the bare null. Spinner with a drag strip while loading; once the list has errored, or has been pending for 15s, shows a message and a Retry wired to the query's refetch(). Mirrors the blocking screens in _authenticated/layout.tsx.
  • resolveWindowOrg.test.ts: table test covering the five cases from the issue plus two edges.

Verification

  • bun test on the new test: 7 pass. With the pre-fix behaviour restored (errored → wait), the "unresolvable" case fails, so the test guards the regression.
  • bun typecheck in apps/desktop: clean.
  • Biome check on the changed folder: clean.
  • No CDP run: this was done in a fresh worktree without a .env/dev stack, so I did not drive the real app to simulate a /api/trpc outage. Manual repro is in the issue (break /api/trpc only, relaunch within the 5-minute cookie-cache window).

Fixes #6794

https://claude.ai/code/session_011XQusXtWEwq2nxN4jfMUpv


Summary by cubic

Show an error screen with Retry instead of a blank window when organization.list fails in the desktop app. Previously CollectionsProvider returned null on both load and error, blanking the frameless window; now a spinner is shown while loading and an error with Retry appears after a failure or 15s.

  • Refactor: extracted org resolution into a pure resolveWindowOrg with three outcomes (wait, unresolvable, resolved); covered by a table test for core and edge cases.
  • UI: added OrgResolutionScreen with spinner, drag strip, and Retry wired to organization.list refetch; used only when the window’s org is unresolved.
  • Behavior: keep the authenticated tree mounted during org switches; never adopt a registry org unless verified by the membership list; fall back to the session org if the registry org is no longer a membership; render “unresolvable” on list failure rather than adopting.
  • Review/QA: focus on the one-shot init semantics (unchanged), the resolution branching, and retry wiring; simulate a transient organization.list outage to see spinner → error → Retry recovery; new tests pass.

Written for commit 053e49c. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Improved organization selection when opening desktop windows.
    • Preserves valid organization context and falls back to the active session when needed.
    • Handles missing or failed organization data without leaving the app blank.
  • New Features

    • Added a loading and error screen with retry support when organization resolution is delayed or unsuccessful.
    • Added coverage for organization resolution scenarios and fallback behavior.

…nization.list fails

A returning window with a registry org rendered nothing when organization.list
errored while auth stayed healthy: CollectionsProvider returned null both while
loading and on error. Extract the three-way decision into resolveWindowOrg and
render OrgResolutionScreen (spinner, then Retry once errored or after 15s)
instead of null. The guard is unchanged: an unverified registry org is never
adopted on failure.

Fixes #6794

Claude-Session: https://claude.ai/code/session_011XQusXtWEwq2nxN4jfMUpv
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 18041746-f9b6-4517-a2a3-d025646a5131

📥 Commits

Reviewing files that changed from the base of the PR and between 7277eb9 and 053e49c.

📒 Files selected for processing (5)
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/CollectionsProvider.tsx
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/components/OrgResolutionScreen/OrgResolutionScreen.tsx
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/components/OrgResolutionScreen/index.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/resolveWindowOrg.test.ts
  • apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/resolveWindowOrg.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

The change adds organization resolution for persisted windows. The provider now distinguishes pending, resolved, and failed states. It adopts only verified organizations and renders a retryable loading or error screen when resolution has no result.

Changes

Window organization resolution

Layer / File(s) Summary
Organization resolution decision flow
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/resolveWindowOrg.ts, apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/resolveWindowOrg.test.ts
resolveWindowOrg waits for pending data, retains valid registry memberships, falls back to the session organization, and reports unresolved failures. Tests cover these result states.
Provider resolution integration
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/CollectionsProvider.tsx
CollectionsProvider tracks organization-list errors, adopts only resolved organizations, preserves existing context during switches, and renders the resolution screen when no context exists.
Resolution and retry screen
apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/components/OrgResolutionScreen/OrgResolutionScreen.tsx, apps/desktop/src/renderer/routes/_authenticated/providers/CollectionsProvider/components/OrgResolutionScreen/index.ts
OrgResolutionScreen displays loading, error, and timeout states, provides retry handling, preserves window dragging, and is re-exported locally.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 053e4

The change replaces a blank desktop window with loading, timeout, error, and retry states when organization loading fails. No actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant CollectionsProvider
  participant organization.list
  participant resolveWindowOrg
  participant OrgResolutionScreen
  CollectionsProvider->>organization.list: Read organizations and errors
  CollectionsProvider->>resolveWindowOrg: Pass window, memberships, loading, errors, and session organization
  resolveWindowOrg-->>CollectionsProvider: Return wait, resolved, or unresolvable
  CollectionsProvider->>OrgResolutionScreen: Render unresolved state with retry
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title uses conventional commit format and clearly identifies the desktop blank-window fix caused by organization.list failure.
Description check ✅ Passed The description explains the problem, fix, testing, issue reference, and known manual-testing limitation with sufficient detail.
Linked Issues check ✅ Passed The changes satisfy issue #6794 by showing an error and Retry state while preserving the unverified registry organization guard.
Out of Scope Changes check ✅ Passed The changes are limited to organization resolution logic, its UI, exports, and focused tests required by issue #6794.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/6794-org-list-error-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ashbrener ashbrener left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I filed #6794, so I went looking for the trap rather than the happy path. It isn't here — the invariant holds.

resolveWindowOrg can't return resolved while a registry org is present and the membership list isn't: line 29 returns unresolvable/wait before anything below it runs, and registryOrgIsStillMine needs organizations != null anyway. The effect then sets initializedRef only on resolved, so an unverified id is rendered and never adopted. That was the way to get this wrong — a transient failure pinning the window to a dead org permanently, with the one-shot ref meaning nothing re-evaluates when the list comes back.

isError is also the right signal rather than status/failureCount: TanStack holds pending through the retry sequence, so the three transport retries still show the spinner and only a settled failure reaches the error screen — which lines up with hostServiceQueryRetry giving tRPC-shaped 500s zero retries. And self-healing survives, since the provider stays mounted and focus/reconnect refetch still resolves.

Three things, none of them correctness:

  1. The tests cover the decision function, not the line that regressed. Reverting the render site to return null reintroduces #6794 with all seven still green. Separately, "waits when nothing can supply an org" isn't reachable in the app — layout.tsx:277 redirects before the provider mounts — while the case that is live and untested is organizations present with isError: true. That's precisely what a later "hoist the errored check to the top" edit would break, and it would blank a working window on a failed background refetch.

  2. The spinner renders unconditionally (OrgResolutionScreen.tsx:27, outside the showRetry block), so "Can't reach the Superset server" sits under an animation while nothing is in flight and nothing will retry until focus or reconnect. The screen this mirrors doesn't do that — layout.tsx:250 swaps in HiOutlineWifi.

  3. The body copy doesn't branch. The heading does, so the 15s-pending case reads "Still loading your organizations" directly above "Superset couldn't load your organizations." Worth noting too that an errored query keeps status: "error" across refetch() — only fetchStatus flips — so clicking Retry changes nothing on screen. isFetching from the same destructure would fix the button state and the spinner gating together.

Drag strip is correctly outside the conditional, so the error window is still movable — easy one to miss on a frameless window.

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.

Blank window when organization.list fails while auth is healthy

2 participants