Skip to content

fix(desktop): gate v2 workspace children on collection readiness#3464

Merged
saddlepaddle merged 1 commit intomainfrom
alert-monitor
Apr 15, 2026
Merged

fix(desktop): gate v2 workspace children on collection readiness#3464
saddlepaddle merged 1 commit intomainfrom
alert-monitor

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Apr 14, 2026

Summary

  • Fixes a race where switching workspaces (or first-load) would crash with useWorkspaceClient must be used within WorkspaceClientProvider. The v2-workspace layout was rendering <Outlet /> without WorkspaceTrpcProvider during the tick between workspaceId changing and the useLiveQuery join resolving — the outgoing page's hooks (TerminalPane, useGitChangeEvents, etc.) would call useWorkspaceClient() against an empty context.
  • Now holds the render until useLiveQuery reports isReady, so the new workspace always mounts into a valid provider on the same tick.
  • Adds an explicit "Workspace not found" state for the case where the collection has hydrated but the id doesn't resolve, instead of silently falling through to a provider-less <Outlet />.

Test plan

  • Open a v2 workspace, rapidly switch between workspaces, confirm no error overlay
  • Open a v2 workspace URL directly on cold start, confirm it loads without flashing the error
  • Navigate to /v2-workspace/<bogus-id> and confirm the "Workspace not found" message shows

Summary by cubic

Fixes a race that crashed v2 workspace pages by delaying child rendering until the workspace collection is ready. Child routes now mount only when data is ready and a workspace exists, preventing provider-less renders.

  • Bug Fixes
    • Gate rendering on useLiveQuery.isReady and a valid workspaceId to avoid the "useWorkspaceClient must be used within WorkspaceClientProvider" crash.
    • Return null during loading to prevent <Outlet /> without WorkspaceTrpcProvider.
    • Show "Workspace not found" when the collection is hydrated but the workspace or host URL is missing.

Written for commit 37e6eff. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed rendering of workspace content before data is fully loaded, preventing incomplete display states
    • Improved error messaging to accurately indicate when a workspace is not found

The v2-workspace layout rendered child routes without WorkspaceTrpcProvider
during the tick between workspaceId changing and the useLiveQuery join
resolving. If the outgoing page hadn't finished unmounting, its hooks
(TerminalPane, useGitChangeEvents, etc.) crashed with "useWorkspaceClient
must be used within WorkspaceClientProvider".

Hold the render until useLiveQuery reports isReady so the new workspace
mounts into a valid provider on the same tick, and show an explicit
"Workspace not found" state when the collection has hydrated but the id
doesn't resolve.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 14, 2026

📝 Walkthrough

Walkthrough

Updated V2WorkspaceLayout component to gate child route rendering on useLiveQuery readiness state. Now returns null when workspaceId is missing or query is not ready, and displays a "Workspace not found" message instead of "Workspace host service not available" when workspace data is unavailable.

Changes

Cohort / File(s) Summary
Layout Readiness Gate
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx
Added isReady check from useLiveQuery to prevent rendering child routes until query is ready. Updated fallback messaging to display "Workspace not found" when workspace or host URL is unavailable.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 A layout that waits for its moment to shine,
No rendering before the data's divine,
With readiness checked and messages clear,
The workspace now knows when its routes appear!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: gating v2 workspace children rendering on collection readiness to fix the race condition.
Description check ✅ Passed The description is comprehensive and well-structured, covering the bug fix, root cause, solution, and explicit test plan, though the template sections are not formally used.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 alert-monitor

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 and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 15, 2026

Greptile Summary

This PR fixes a race condition in the v2-workspace layout that caused crashes with useWorkspaceClient must be used within WorkspaceClientProvider during workspace switches and cold-start loads. The layout now gates rendering on useLiveQuery's isReady flag instead of rendering a bare <Outlet /> during the window between workspaceId changing and the collection resolving. It also consolidates the not-found handling into a single explicit error state instead of silently falling through to a provider-less outlet.

Changes:

  • Extracts isReady from useLiveQuery and returns null (instead of <Outlet />) when the collection hasn't hydrated yet or workspaceId is absent — ensuring WorkspaceTrpcProvider always wraps children before they mount.
  • Merges the !workspace and !hostUrl guards into a single "Workspace not found" fallback, removing the old TODO comment that documented the known crash.
  • Two non-blocking P2 suggestions remain: misleading error message for the host-unavailable case, and no loading indicator during hydration.

Confidence Score: 4/5

Safe to merge — the crash is eliminated and the core logic is correct; remaining issues are P2 UX polish.

The fix correctly uses isReady to gate WorkspaceTrpcProvider rendering, resolving the documented race condition. The two P2 comments (misleading error message for host-unavailable case, blank screen during loading) are non-blocking improvements that don't affect correctness or stability.

No files require special attention beyond the single changed layout file.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx Core fix is correct — isReady gate eliminates the provider-less <Outlet /> race; minor issue: merging !workspace and !hostUrl into one message means a local workspace with a temporarily-unavailable host service shows "Workspace not found" instead of a more accurate host error, and null during loading gives no visual feedback.

Sequence Diagram

sequenceDiagram
    participant Router as TanStack Router
    participant Layout as V2WorkspaceLayout
    participant LiveQuery as useLiveQuery
    participant Provider as WorkspaceTrpcProvider
    participant Child as Child Routes

    Router->>Layout: workspaceId changes (navigation)
    Layout->>LiveQuery: query(collections, workspaceId)
    Note over LiveQuery: isReady = false
    LiveQuery-->>Layout: data=[], isReady=false
    Layout-->>Router: return null (no render, no crash)

    LiveQuery-->>Layout: data=[workspace], isReady=true
    alt workspace and hostUrl resolved
        Layout->>Provider: mount WorkspaceTrpcProvider
        Provider->>Child: render Outlet safely
    else workspace missing or hostUrl null
        Layout-->>Router: show Workspace not found
    end
Loading

Reviews (1): Last reviewed commit: "fix(desktop): gate v2 workspace children..." | Re-trigger Greptile

Comment on lines +71 to 76
if (!workspace || !hostUrl) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace host service not available
Workspace not found
</div>
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Misleading error message when host service is unavailable

The two sub-cases covered by !workspace || !hostUrl have meaningfully different root causes:

  1. !workspace — the id doesn't resolve in the collection → "Workspace not found" is correct.
  2. !hostUrl while workspace != null — this only happens for a local workspace when activeHostUrl is null (i.e., the host service hasn't started or has lost its connection). In that scenario the workspace does exist, but showing "Workspace not found" is misleading.

Before this PR the two cases had distinct messages ("Workspace host service not available" vs. silent <Outlet />). Splitting them again makes the error actionable:

Suggested change
if (!workspace || !hostUrl) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace host service not available
Workspace not found
</div>
);
if (!workspace) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace not found
</div>
);
}
if (!hostUrl) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace host service not available
</div>
);
}

Comment on lines +67 to 69
if (!workspaceId || !isReady) {
return null;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 No loading indicator during collection hydration

Returning null while !isReady eliminates the crash, but it also produces a blank screen during the initial load and every workspace transition. Depending on how long isReady takes to flip, users may see a momentary flash of nothing. A lightweight loading state (spinner, skeleton, or even a static placeholder) would avoid that:

Suggested change
if (!workspaceId || !isReady) {
return null;
}
if (!workspaceId || !isReady) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Loading workspace…
</div>
);
}

This is a non-blocking suggestion; the crash fix is the important part.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx`:
- Around line 71-75: The current conditional lumps both missing workspace and
missing host into the same message; update the JSX in the layout component so
that the check for workspace and hostUrl are separate: when !workspace return
the existing "Workspace not found" fallback, but when workspace exists and
!hostUrl return a distinct fallback (e.g., "Host unavailable" or "Waiting for
host") so a valid workspace isn't mistaken for a bad ID; adjust the return logic
in the component that references the workspace and hostUrl variables
accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 49dbf33b-ec89-4d0c-8728-901e7dbc2d4a

📥 Commits

Reviewing files that changed from the base of the PR and between 2bf1049 and 37e6eff.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx

Comment on lines +71 to 75
if (!workspace || !hostUrl) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace host service not available
Workspace not found
</div>
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.

⚠️ Potential issue | 🟡 Minor

Keep the missing-workspace and missing-host states separate.

Line 71 now maps both !workspace and !hostUrl to "Workspace not found". That makes a valid workspace with an unavailable or still-bootstrapping local host look like a bad ID. Please keep the new not-found state for !workspace, but preserve a distinct fallback for !hostUrl.

🔧 Suggested split
-if (!workspace || !hostUrl) {
+if (!workspace) {
   return (
     <div className="flex h-full w-full items-center justify-center text-muted-foreground">
       Workspace not found
     </div>
   );
 }
+
+if (!hostUrl) {
+  return (
+    <div className="flex h-full w-full items-center justify-center text-muted-foreground">
+      Workspace host service not available
+    </div>
+  );
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!workspace || !hostUrl) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace host service not available
Workspace not found
</div>
if (!workspace) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace not found
</div>
);
}
if (!hostUrl) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace host service not available
</div>
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx`
around lines 71 - 75, The current conditional lumps both missing workspace and
missing host into the same message; update the JSX in the layout component so
that the check for workspace and hostUrl are separate: when !workspace return
the existing "Workspace not found" fallback, but when workspace exists and
!hostUrl return a distinct fallback (e.g., "Host unavailable" or "Waiting for
host") so a valid workspace isn't mistaken for a bad ID; adjust the return logic
in the component that references the workspace and hostUrl variables
accordingly.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx:71">
P2: This condition conflates two distinct error states: `!workspace` (ID doesn't resolve) and `!hostUrl` (host service unavailable while workspace exists). Showing "Workspace not found" when the host service is down is misleading — the workspace does exist. Split into separate checks to preserve the actionable "Workspace host service not available" message that existed before this change.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +71 to 76
if (!workspace || !hostUrl) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace host service not available
Workspace not found
</div>
);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 15, 2026

Choose a reason for hiding this comment

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

P2: This condition conflates two distinct error states: !workspace (ID doesn't resolve) and !hostUrl (host service unavailable while workspace exists). Showing "Workspace not found" when the host service is down is misleading — the workspace does exist. Split into separate checks to preserve the actionable "Workspace host service not available" message that existed before this change.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/layout.tsx, line 71:

<comment>This condition conflates two distinct error states: `!workspace` (ID doesn't resolve) and `!hostUrl` (host service unavailable while workspace exists). Showing "Workspace not found" when the host service is down is misleading — the workspace does exist. Split into separate checks to preserve the actionable "Workspace host service not available" message that existed before this change.</comment>

<file context>
@@ -64,22 +64,14 @@ function V2WorkspaceLayout() {
 	}
 
-	if (!hostUrl) {
+	if (!workspace || !hostUrl) {
 		return (
 			<div className="flex h-full w-full items-center justify-center text-muted-foreground">
</file context>
Suggested change
if (!workspace || !hostUrl) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace host service not available
Workspace not found
</div>
);
if (!workspace) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace not found
</div>
);
}
if (!hostUrl) {
return (
<div className="flex h-full w-full items-center justify-center text-muted-foreground">
Workspace host service not available
</div>
);
}
Fix with Cubic

@saddlepaddle saddlepaddle merged commit 57557f8 into main Apr 15, 2026
7 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

MocA-Love pushed a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
…erset-sh#3464)

The v2-workspace layout rendered child routes without WorkspaceTrpcProvider
during the tick between workspaceId changing and the useLiveQuery join
resolving. If the outgoing page hadn't finished unmounting, its hooks
(TerminalPane, useGitChangeEvents, etc.) crashed with "useWorkspaceClient
must be used within WorkspaceClientProvider".

Hold the render until useLiveQuery reports isReady so the new workspace
mounts into a valid provider on the same tick, and show an explicit
"Workspace not found" state when the collection has hydrated but the id
doesn't resolve.
MocA-Love added a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
MocA-Love added a commit to MocA-Love/superset that referenced this pull request Apr 15, 2026
-s ours merge to record that upstream commits a3e34bf through
de70163 (13 commits) are semantically already present on origin/main
via the PR1-6 cherry-pick series (PRs #176, #177, #178, #179, #180,
#182), plus fork-adaptation fixes layered on top.

This merge target is de70163 specifically (not upstream/main) so
newer upstream commits (9fff075 and later) remain visible in future
behind counts.

Upstream commits covered by this audit merge:
- a3e34bf  fix(desktop): restore cmd+click requirement for v1 terminal file links (superset-sh#3457)  [PR1/#176]
- 57557f8  fix(desktop): gate v2 workspace children on collection readiness (superset-sh#3464)       [PR1/#176]
- 4ee2e61  fix(desktop): use native clipboard for copy path in v2 sidebar (superset-sh#3462)         [PR1/#176]
- 87d6e93  feat(desktop): close settings with Escape key (superset-sh#3466)                          [PR1/#176]
- 9c7f5f4  chore(desktop): auto-restart host-service on bundle change in dev (superset-sh#3461)      [PR1/#176]
- 93140d9  fix(mcp): accept MCP resource URL as valid OAuth audience (superset-sh#3459)              [PR2/#177]
- be9e000  fix(desktop): drive tray menu off events, fetch real org name (superset-sh#3458)          [PR2/#177]
- c5f791e  feat(v2): unify workspace delete through host-service (superset-sh#3443)                  [PR3/#178]
- 2c24d93  feat(desktop): paginated branch picker with checkout + open actions (superset-sh#3397)    [PR4/#179]
- 2bf1049  feat(desktop/hotkeys): v1 directional pane focus + best-effort v1 override migrator (superset-sh#3460)  [PR5/#180]
- 1294a7d  feat(desktop/hotkeys): restore Cmd+Alt+Arrow for tab/workspace nav (superset-sh#3472)    [PR5/#180]
- de70163  feat(desktop): v2 review tab first pass — PR info, checks, comments (superset-sh#3463)    [PR6/#182]

Intentionally skipped (version bump, fork has independent versioning):
- 1e23353  chore(desktop): bump version to 1.5.5 (superset-sh#3473)

Fork-adaptation fixes layered on top of the cherry-picks:
- PR1: host-service-coordinator alias import fix, settings Escape
       selector narrowing (role-based + popper wrapper), Escape
       close uses replace navigation
- PR2: dual quit mode preservation (requestQuit "release"/"stop"),
       trayUpdateToken guard for stale async fetchHostInfo results
- PR4: ChangesHeader.normalizeBranchName regex rewrite (lint false
       positive), worktree add uses fullRef for remote-tracking
       refs, syncTimedOut reset on pendingId change, GIT_REFS.md
       barrel example fix
- PR5: migrate.ts re-sanitize of existing localStorage overrides
       (v2 marker bump intent), FOCUS_PANE_* enabled:isActive for
       KeepAliveWorkspaces, CATEGORY_ORDER merges Navigation (upstream)
       and Browser (fork)
- PR6: normalizeThreadsToComments flattens all thread.comments (not
       just first), CommentPane overrides <a> (openUrl) and <img>
       (SafeImage), zero-badge suppression, pr-null comments gate

Fork features verified intact (Explore agent audit of combined
36d4de4..35d95f3 range):
- BROWSER_RELOAD / BROWSER_HARD_RELOAD hotkeys
- dual quit mode menu in tray
- v1 terminal cold-restore + retry reconnect (out of range but
  unaffected)
- KeepAliveWorkspaces (FOCUS_PANE_* gated on isActive)
- useCommandPalette + addMemoTab in v2 workspace
- host-service-coordinator rename alias pattern
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.

1 participant