Skip to content

fix(app): Review pane stays on loading for file tree and changes #50119

Closed

Description

@RsLuna7

Summary

Desktop Review can sit forever on both panes:

  • Sidebar: Filter files (筛选文件), then 加载中...
  • Preview: Loading changes (正在加载更改…)

This is one shared pending flag, not two independent loaders. Related but not the same as #49260 (non-git directory) and #42964 (eager full diffs making Desktop unresponsive).

Environment

  • OS: Windows 10/11
  • Client: OpenCode Desktop Review tab (session-ui v2 sidebar + preview)
  • Workspace: git repository (not a non-git folder)

Reproduction

  1. Open a session in Desktop for a git project.
  2. Click Review.
  3. Leave the panel open.

Observed: left tree and right diff stay on loading copy with no empty state and no error.

A heavy working tree makes this easy to hit. git status --porcelain --untracked-files=all plus per-untracked git diff --no-index will run against every untracked file. A local Electron unpack directory with thousands of files is enough to keep the request outstanding.

Expected

  • Bound the VCS request; if it fails or times out, show an error, not a spinner.
  • If there is nothing to review, show the empty state.
  • Do not treat isPending as still loading when the query is disabled or never started.

Actual

Both panes key off createSessionReview().ready():

const ready = () => {
  if (mode() === "git" || mode() === "branch") return !vcsQuery.isPending
  return true
}
  • Sidebar loading: ReviewPanelSidebar diffsReady fallback (common.loading).
  • Preview loading: ReviewPanelEmpty when mode is git/branch and !ready() (session.review.loadingChanges).

vcsQuery has no timeout. ready() ignores isError / fetchStatus. TanStack Query v5 keeps isPending === true when there is no cached data even if enabled is false, so a disabled query (disconnected, missing project.vcs, review not yet wanted) also looks like an in-flight load.

Working-tree diffs then call git status --untracked-files=all and may statUntracked / patchUntracked (git diff --no-index) per ?? path in packages/core/src/plugin/vcs/git.ts. That can run for a long time on large untracked trees, so isPending never clears.

Suggested fix

  1. Treat loading as isFetching && !isFetched (or fetchStatus === "fetching"), not raw isPending.
  2. Add an error empty state when isError.
  3. Cap/timeout GET /api/vcs/diff; skip or bound untracked blobs instead of one git diff --no-index per file.
  4. Keep ignored build/unpack dirs out of status (--untracked-files=normal or respect ignore rules strictly).

Activity

  1. 13579ADGJLzcbm commented on Oct 27, 2026

    @13579ADGJLzcbm

    Confirming on Desktop 2.1.4 (Windows 11, bundled CLI 2.1.4). The heavy-working-tree path here does more than leave the Review pane loading: it black-screens the entire session view.

    Environment

    • OpenCode Desktop 2.1.4 (Electron 44.9.1), Windows 11 IoT LTSC, AMD Vega 8
    • Repo: 42,440 untracked files (git status --porcelain -uall), 57,666 files on disk
    • Review side pane was persisted open for the session (tabs.panes.<key>.review = true)

    Repro

    1. Open a git repo with tens of thousands of untracked files.
    2. Open a session whose Review pane was left open.
    3. The tab strip paints, but the session content area is pure black: no transcript, no composer.

    Measurements (local service, Basic auth)

    • GET /api/vcs/diff?location[directory]=<repo>&mode=working -> no response within 60s (client aborts at 60s). Call it again and it hangs again.
    • GET /api/session/<id>/diff -> 200, 4.9 MB, 2.96s for a session that had touched a single 945 KB one-line file.
    • renderer.log: [server-request-queue] server thrashing detected every 10s, plus continuous ResizeObserver loop completed with undelivered notifications.
    • Clearing the persisted Review-open flag makes the transcript render immediately, without touching anything else. So the session view is being held behind the Review diff query.

    Root-cause pointers (HEAD)

    • packages/gui-plugins/src/review/model.ts:127 — const chosen = stored()?.mode ?? "git"; every session defaults to the whole working tree.
    • :164 — enabled: ... && wantsReview() && ...; this fires on mount when the side pane is persisted open.
    • :186 — the details query also calls vcs.diff({ mode: "working" }) unbounded.
    • :239 — rows() returns the full array; no file-count or patch-size cap before rendering.
    • Server side, mode=working enumerates every untracked path, so 42k untracked files is an effectively unbounded request.

    Workaround I applied locally (patched app.asar, not upstream)

    • default mode "git" -> "turn" (session diff only)
    • drop patches >400 KB and cap the render list at 500 files
    • clear persisted tabs.panes.*.review so Review starts closed

    Suggested fix

    • Default Review to the session/turn diff; make the whole-worktree diff an explicit choice.
    • Bound and cancel the VCS request: max untracked files, max patch bytes, request timeout; show an error/empty state instead of an endless spinner.
    • Never suspend the session transcript behind the Review query.
    • Do not start the Review request just because the pane was persisted open; wait until it is actually visible.

    Related: #42964 (closed, eager full-file diffs), #51216 (service kill/restart loop when snapshot blocks /api/info), #57441.

  2. Dante-dan commented on Oct 27, 2026

    @Dante-dan
    Contributor

    The remaining plain working-tree query noted in the #59526 analysis is the session-location menu's change summary. I wired that query through the untracked=folders API added by #57527, matching the Review list and avoiding per-file patches for files inside untracked directories. The existing project-menu keeper now checks the actual request parameter.

    Fixed compare. This covers the location-menu request; the persisted-open Review blackout still needs Desktop/Windows validation.

    bun run check (35 tasks) and lint:changed pass on Bun 1.4.4 / Node 24.12. Local Playwright launch is denied by macOS MachPort permissions, so I could not execute the browser assertion. The existing VCS folder fixture hit its 30-second timeout; typecheck:e2e also reports 11 diagnostics in untouched test/performance files.

  3. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the detailed report. I tried this on Linux with @opencode/[email protected]: a git repo with about 23,000 untracked files (40 folders of 500 files plus 3,000 loose files), calling GET /api/vcs/diff?mode=working on the server directly. The request finished in about 4 seconds and returned around 5.6 MB, so I couldn't get it to hang. I can't run the Windows Desktop app here, and the slowness seems to depend on Windows, where each git process starts much more slowly. The >60s measurement in the comment above fits that.

    I couldn't check the UI side either: whether the Review panes stay on loading while that request is slow, or after it fails or the server disconnects. That needs someone with Windows and the Desktop app.

    Closely related:

    If you can, these would help a maintainer reproduce it:

    • your exact OpenCode Desktop version
    • roughly how many untracked files git status --porcelain --untracked-files=all lists
    • whether the panes ever leave the loading state if you wait a few minutes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions