Skip to content

desktop: renderer unresponsive - infinite layout loop (scrollToEnd and resizeItem) in virtualized message list #58127

Closed

Description

@Tecnico-de-Suporte-II

Summary

OpenCode Desktop (Windows) renderer becomes permanently unresponsive on startup when it auto-restores a tab pointing to a very large session. Stack samples show an infinite layout loop between the virtualized message list's auto-scroll (scrollToEnd → scrollToIndex) and resizeItem → Map.forEach → reactive update → scrollToEnd again. The window thread never yields, so Electron fires renderer-unresponsive repeatedly and shows the "OpenCode is not responding" dialog. Reproduced on 2.1.2 and 2.1.3 (Electron 44.9.1 / Chrome 152).

Environment

  • opencode version: desktop app 2.1.3 (also reproduced on 2.1.2 before updating); CLI 2.1.3
  • OS: Windows_NT 10.0.19045 (win32 x64)
  • Terminal: Unavailable: desktop Electron app (no terminal involved)
  • Shell: PowerShell 5.1
  • Install/channel: latest (packaged desktop install, auto-updater)
  • Active plugins: none found in config

Reproduction

  1. Use OpenCode Desktop with a long-running session containing a very large number of message parts (session that accumulated a ~7 MB server log).
  2. Close and reopen the desktop app; it restores the tab via localStorage key opencode.desktop.window.<windowId>.last-active-url, which points at /server/<server>/session/<ses_…> (the large session).
  3. Within ~10–20 s of window load, the renderer stops responding; the "OpenCode is not responding" dialog appears (Reabrir / Exportar logs / Continuar aguardando).
  4. "Reabrir" reloads the same restored tab and the loop re-triggers.

Expected Behavior

The session view should render (even if slowly), and/or the auto-scroll logic should not feed back into item-resize observers in an unbounded loop. There should also be a way to disable session/tab restore on startup.

Actual Behavior

UI thread is stuck in a synchronous loop; the window is dead until killed. Exported window.log reports renderer unresponsive with these samples (identical shape on 2.1.2 and 2.1.3, only bundle names differ):

[error] (window) renderer unresponsive { currentURL: 'oc://renderer/index.html' }
<4> at Te (oc://renderer/assets/route-*.js:9:58274)
<3> at Array.Re (main-*.js:2:12237)
    at Object.active (route-*.js:10:9062)
    at n (route-*.js:9:52592)
    at Object.scrollToFn (route-*.js:9:54800)
    at w._scrollToOffset (esm-*.js:1:20352)
    at w.scrollToIndex (esm-*.js:1:18902)
    at Proxy.scrollToEnd (esm-*.js:1:19335)
    at Te (route-*.js:9:58258)
<1> at Map.delete (<anonymous>)
    at Fg.F.resizeItem (route-*.js:9:55294)
    at route-*.js:9:58629
    at Map.forEach (<anonymous>)
    at route-*.js:9:58600
<1> at Fg.F.resizeItem (route-*.js:9:55184)
    ... (same cycle)
Total Samples: 14

The pattern scrollToEnd → scrollToIndex → resizeItem → reactive update → scrollToEnd repeats across every sample, indicating an unbounded render/layout feedback loop in the virtualized message list.

Additional Context

  • Fully reproducible on every start while the large session is the restored tab; the app becomes usable only after clearing the restore state.
  • Workarounds tried: holding Shift at launch (does not prevent restore); updating 2.1.2 → 2.1.3 (loop persists); clearing/replacing the Local Storage folder finally stops the freeze.
  • Suggest: (a) break the loop (e.g. guard auto-scroll while resizing / rAF-scheduled scroll, item-size measurement caching), (b) add a setting or startup flag to disable tab/session restore, (c) a fallback to open a new-session tab if the restored route triggers the unresponsive watchdog.
  • Debug log bundles (window.log / main.log / renderer.log samples) available on request; they contain full renderer unresponsive stacks and netlogs (several privacy-redacted identifiers).

Activity

  1. opencode-agent commented on Oct 22, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report and the stack samples. I couldn't reproduce this here: it happens in the desktop app's renderer, and this sandbox can't run the desktop app or build a session large enough to trigger it, so a maintainer with the desktop app will need to look.

    The samples point at the auto-scroll-to-end logic in the session timeline's virtualized list. It looks like it keeps triggering item re-measurement in a loop when the session is pinned to the bottom, but I haven't confirmed that by running it.

    If you can, attaching the exported window.log / renderer.log, plus a rough count of messages and tool calls in the affected session, would help whoever picks this up. Until then, clearing the window's restore state (as you found) is the workaround.

  2. Hona commented on Oct 30, 2026

    @Hona
    Member

    Fixed in v2.2.1+ (#60034). Under fractional OS display scaling (125%/150%/175%) or desktop zoom, Chromium stops scrolling ~1-2px short of scrollHeight - clientHeight, which wedged the timeline virtualizer cold-mount settle microtask loop (scrollToEnd <-> resizeItem) on session open/restore. Please update to v2.2.1 or newer (v2.2.2 is current) and let us know if you still see this.

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