Windows Desktop: SSE event stream silently drops, UI stops updating (requires Ctrl+R to refresh) #9361

Open
opened 2026-02-16 18:12:16 -05:00 by yindo · 0 comments
Owner

Originally created by @brunusansi on GitHub (Feb 14, 2026).

Originally assigned to: @adamdotdevin on GitHub.

Bug Description

On Windows Desktop, the SSE (Server-Sent Events) connection between the WebView2 frontend and the local sidecar server silently drops during use. When this happens, the UI stops receiving realtime updates — messages are processed by the backend but the UI doesn't reflect them. The only workaround is pressing Ctrl+R to force a page reload, which re-establishes the SSE connection.

Root Cause Analysis

After tracing through the source code, I identified the issue in packages/app/src/context/global-sdk.tsx:

const eventFetch = (() => {
  if (!platform.fetch) return
  try {
    const url = new URL(server.url)
    const loopback = url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1'
    if (url.protocol === 'http:' && !loopback) return platform.fetch
  } catch {
    return
  }
})()

For desktop connections (always localhost):

  • loopback evaluates to true
  • The condition url.protocol === 'http:' && !loopback is false
  • eventFetch returns undefined
  • The SSE stream falls back to WebView2's native fetch instead of Tauri's platform.fetch

WebView2's native fetch has known reliability issues with long-lived SSE connections on localhost. The connection drops silently, and while the reconnection loop (RECONNECT_DELAY_MS = 250ms) attempts to re-establish it, the same underlying issue causes repeated failures.

The SSE heartbeat in server.ts is set to 30 seconds with a comment referencing WKWebView's 60-second timeout — but WebView2 on Windows has different behavior that this heartbeat doesn't address.

Suggested Fix

Force platform.fetch (Tauri's native HTTP client) for SSE connections on localhost when running in the desktop app:

const eventFetch = (() => {
  if (!platform.fetch) return
  try {
    const url = new URL(server.url)
    const loopback = url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1'
    // Use platform.fetch for ALL connections in desktop mode,
    // including loopback, to avoid WebView2 SSE reliability issues
    if (url.protocol === 'http:') return loopback ? platform.fetch : platform.fetch
  } catch {
    return
  }
})()

Or more simply, always return platform.fetch when available:

const eventFetch = platform.fetch ?? undefined

This would bypass WebView2's native fetch entirely for the SSE stream and use Tauri's HTTP plugin, which doesn't suffer from the same connection dropping behavior.

Workaround

Setting WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS environment variable before launching the app mitigates the issue:

WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--disable-background-timer-throttling --disable-renderer-backgrounding --disable-features=IntensiveWakeUpThrottling,BackForwardCache --disable-http2

Environment

  • OS: Windows 10/11
  • OpenCode Desktop: v1.2.2
  • Frequency: Every session, from the start — not intermittent

Related Issues

  • #9577 (White screen on launch — Windows 10)
  • #11322 (Blank on launch — Windows 11)
  • #12240 (Orphaned process accumulation — related sidecar cleanup issue)
  • #12885 (Session not found — Windows)
Originally created by @brunusansi on GitHub (Feb 14, 2026). Originally assigned to: @adamdotdevin on GitHub. ## Bug Description On Windows Desktop, the SSE (Server-Sent Events) connection between the WebView2 frontend and the local sidecar server silently drops during use. When this happens, the UI stops receiving realtime updates — messages are processed by the backend but the UI doesn't reflect them. The only workaround is pressing Ctrl+R to force a page reload, which re-establishes the SSE connection. ## Root Cause Analysis After tracing through the source code, I identified the issue in `packages/app/src/context/global-sdk.tsx`: ```typescript const eventFetch = (() => { if (!platform.fetch) return try { const url = new URL(server.url) const loopback = url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1' if (url.protocol === 'http:' && !loopback) return platform.fetch } catch { return } })() ``` For desktop connections (always localhost): - `loopback` evaluates to `true` - The condition `url.protocol === 'http:' && !loopback` is **false** - `eventFetch` returns `undefined` - The SSE stream falls back to **WebView2's native fetch** instead of Tauri's `platform.fetch` WebView2's native fetch has known reliability issues with long-lived SSE connections on localhost. The connection drops silently, and while the reconnection loop (`RECONNECT_DELAY_MS = 250ms`) attempts to re-establish it, the same underlying issue causes repeated failures. The SSE heartbeat in `server.ts` is set to 30 seconds with a comment referencing WKWebView's 60-second timeout — but WebView2 on Windows has different behavior that this heartbeat doesn't address. ## Suggested Fix Force `platform.fetch` (Tauri's native HTTP client) for SSE connections on localhost when running in the desktop app: ```typescript const eventFetch = (() => { if (!platform.fetch) return try { const url = new URL(server.url) const loopback = url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1' // Use platform.fetch for ALL connections in desktop mode, // including loopback, to avoid WebView2 SSE reliability issues if (url.protocol === 'http:') return loopback ? platform.fetch : platform.fetch } catch { return } })() ``` Or more simply, always return `platform.fetch` when available: ```typescript const eventFetch = platform.fetch ?? undefined ``` This would bypass WebView2's native fetch entirely for the SSE stream and use Tauri's HTTP plugin, which doesn't suffer from the same connection dropping behavior. ## Workaround Setting `WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS` environment variable before launching the app mitigates the issue: ``` WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS=--disable-background-timer-throttling --disable-renderer-backgrounding --disable-features=IntensiveWakeUpThrottling,BackForwardCache --disable-http2 ``` ## Environment - **OS**: Windows 10/11 - **OpenCode Desktop**: v1.2.2 - **Frequency**: Every session, from the start — not intermittent ## Related Issues - #9577 (White screen on launch — Windows 10) - #11322 (Blank on launch — Windows 11) - #12240 (Orphaned process accumulation — related sidecar cleanup issue) - #12885 (Session not found — Windows)
yindo added the windowsweb labels 2026-02-16 18:12:16 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#9361