Skip to content

Desktop 2.0.20 (macOS): file diff view throws on partial diffs — "loadDiffFiles is required" #52263

Closed

Description

@dkattan

Description

The desktop app's file-diff view crashes on any partial (truncated) diff with:

Error: FileDiff: loadDiffFiles is required to load full files for a partial diff
    at gv.loadFilesIfNecessary (oc://renderer/assets/file-CsiGC-aR.js:307:50061)
    at gv.render (oc://renderer/assets/file-CsiGC-aR.js:307:54462)
    at Object.draw (oc://renderer/assets/file-CsiGC-aR.js:767:5250)

Large diffs are served truncated (isPartial: true), and the view's loadFilesIfNecessary() hard-throws when options.loadDiffFiles is missing. In the shipped 2.0.20 renderer bundle (file-CsiGC-aR.js), the string loadDiffFiles appears exactly once — this consume site — so no caller ever provides it and every partial diff throws on render. The hydration path it feeds (loadFilesForDiff → handleFilesLoaded) exists but is never wired to a loader. Small diffs render fine, so the crash looks intermittent.

This started with the 2.0.20 auto-update; 2.0.19 did not have it. Differs from #44918 (that one is a renderer hang from a synchronous line-diff on v1; this is a throw from a missing option on v2).

Steps to reproduce

  1. Open a repo with a working-tree change large enough to be truncated server-side
  2. Open the file diff view for it
  3. View throws instead of rendering

Suggested fix

Pass a loadDiffFiles callback when constructing the view — it receives the partial FileDiff and should fetch the full diffs (e.g. via the VCS diff API) for handleFilesLoaded to merge. As a fallback, rendering the truncated diff with a "truncated" notice beats throwing from render().

Local workaround that confirmed the diagnosis: patching the shipped app.asar to replace the throw with a console.warn + early return makes partial diffs render truncated instead of crashing.

Environment

  • OpenCode version: desktop 2.0.20 (auto-updated from 2.0.19)
  • OS: macOS (Apple Silicon, Darwin 27.2.0 / macOS 26)
  • Terminal: desktop app (Electron)
  • Plugins: opencode-models-discovery

Activity

  1. github-actions commented on Sep 30, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of an existing issue. Please check:

    • #52213: Reports the same Desktop 2.0.20 macOS crash when rendering partial diffs because the loadDiffFiles callback is missing.
  2. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the detailed report. I've confirmed this is a regression from 2.0.19, and it still happens on the latest dev (86ce19d).

    I couldn't run the desktop app here, so I reproduced it with the diff library the app uses (@pierre/diffs), set up the way the review pane sets it up:

    • A normal git diff of a modified file with more than one hunk is turned into a partial diff. This doesn't need a very large change.
    • When the review pane's "show all lines" (expand) mode is on, the app turns on expandUnchanged for the diff view. Since @pierre/diffs 1.5 (shipped in 2.0.20), rendering a partial diff with that option and without loadDiffFiles throws FileDiff: loadDiffFiles is required to load full files for a partial diff.
    • The app never passes loadDiffFiles, so it crashes. @pierre/diffs 1.2.10, the version 2.0.19 used, renders the same diff fine.

    The script below reproduces it: with expand off the diff renders, and with expand on it throws.

    Possible workaround until it's fixed: switch the review pane back to "hide non-diff lines" (collapse) mode. Partial diffs render normally there.

    Related: #52213 (your earlier report of the same problem), and #57441 / #58881 (blank diff pane reports, possibly the same root cause).

    Reproduction script (repro.ts)
    // Reproduces #52263 at the library level: the desktop review pane renders a partial
    // (hunk-only) "change" diff through @pierre/diffs FileDiff with `expandUnchanged`
    // set from the review "expand" mode, but never passes `loadDiffFiles`.
    import { GlobalRegistrator } from "@happy-dom/global-registrator"
    GlobalRegistrator.register()
    const { FileDiff, parsePatchFiles } = await import("@pierre/diffs")
    
    // A modified file with two separated hunks, as produced by `git diff` on a large change.
    const old = Array.from({ length: 60 }, (_, i) => `line ${i + 1}`)
    const patch = [
      "diff --git a/big.ts b/big.ts",
      "index 1111111..2222222 100644",
      "--- a/big.ts",
      "+++ b/big.ts",
      "@@ -4,3 +4,3 @@",
      " line 4",
      "-line 5",
      "+line 5 changed",
      " line 6",
      "@@ -50,3 +50,3 @@",
      " line 50",
      "-line 51",
      "+line 51 changed",
      " line 52",
      "",
    ].join("\n")
    const fileDiff = parsePatchFiles(patch)[0]!.files[0]!
    console.log("type:", fileDiff.type, "isPartial:", fileDiff.isPartial)
    
    for (const expandUnchanged of [false, true]) {
      const container = document.createElement("div")
      document.body.appendChild(container)
      const view = new FileDiff({ expandUnchanged, hunkSeparators: "simple" })
      try {
        view.render({ fileDiff, containerWrapper: container })
        console.log(`expandUnchanged=${expandUnchanged}: rendered OK`)
      } catch (e) {
        console.log(`expandUnchanged=${expandUnchanged}: THROWS ->`, (e as Error).message)
      }
    }

    Run it: bun repro.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions