You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Session state stale after device sleep/resume — heartbeat mismatch causes premature SSE disconnect #17769
After a device resumes from sleep (closing laptop lid, phone lock screen, browser tab backgrounded for extended period), the web UI often shows stale session state — messages may be missing, the session appears stuck, or the UI shows "Unable to retrieve session". The connection does recover eventually but the state is often inconsistent.
Root cause: server/client heartbeat mismatch
The server sends SSE heartbeats every 30 seconds (server.ts:692), but the client expects a heartbeat within 15 seconds (global-sdk.tsx:108 HEARTBEAT_TIMEOUT_MS = 15_000). During normal operation this works because regular events (message updates, status changes) reset the client timer. But during idle periods with no activity, the client will timeout and abort the SSE connection between heartbeats — causing unnecessary reconnection churn even without a sleep/resume event.
Reconnection after sleep/resume:
Device sleeps → SSE connection goes stale, TCP socket may be closed by OS
Since lastEventAt is from before sleep, Date.now() - lastEventAt >> 15s → attempt?.abort() fires
SSE reconnect loop starts (250ms delay) → new connection → server sends server.connected
Client re-bootstraps all directories (sessions, messages) — this part works
But global state (config, providers, auth) is NOT re-fetched on reconnect — only directory-level data is refreshed via queue.push(directory)
Any session that was mid-stream (assistant generating a response) when sleep happened loses that streaming state — the partial message is frozen and no further deltas arrive
Additional issue: SDK SSE client has no max retry attempts by default
The SDK's createSseClient (serverSentEvents.gen.ts:225) only limits retries if sseMaxRetryAttempts is explicitly set. The global-sdk wrapper does not set this parameter — it uses its own reconnect loop (line 124-191) that wraps the SDK stream. However, if the SDK's internal retry loop gets stuck (e.g. server returns 502 repeatedly), the exponential backoff can reach 30s between attempts while the outer loop also waits 250ms — leading to confusing double-retry behavior.
Steps to reproduce
Open opencode serve web UI on a phone or laptop
Start a session and send a message
While the assistant is responding, lock the device / close the lid / switch tabs for 30+ seconds
Resume the device / reopen the tab
Observe: the assistant response may be frozen, or the session shows stale data
OpenCode version
1.2.26
Operating System
Any (macOS, iOS Safari, Android Chrome, Linux)
Terminal
N/A (web UI via opencode serve)
Reacted by Egor Blinov, Haye, Anchit Bajaj, Alex Oksenenko, Chris Yang, Andre-Philippe Paquet and ropepop
OpenCode hosted on a remote VM, exposed via public URL
Accessing through Chrome on Android, also installed as PWA (Add to Home Screen)
Model: claude-opus-4-6 + max variant
Browser tab: switching to any other app or locking the phone kills the SSE stream. When I come back, the conversation is frozen — I have to pull-to-refresh or manually reload the page to see the completed response.
PWA: even worse — switching away from the PWA means I have to force-close and reopen it entirely. There's no pull-to-refresh in PWA mode, so the only recovery is killing the app.
This is the single biggest usability pain point on mobile. A reconnect that automatically re-fetches the full session state (like #13947's fix suggested — treating server.connected as global.disposed to trigger a full refresh) would solve it.
Reacted by Egor Blinov, Haye, Anchit Bajaj, Andre-Philippe Paquet and ropepop
Have same issue:
Opencode serve on VM, using web ui via clodflare/zrok/pinggy.
After sending message i have to manually refresh page to see models answers. After 15 seconds event error
Would it be possible to add a refresh button or a better tombstone mechanism? On the web version I can at least manually refresh the page, but as a PWA app I'm stuck staring at a frozen screen with no way to recover.
Reacted by Chris Yang, Andre-Philippe Paquet and ropepop
Activity
anas-asghar4831 commentedon Apr 4, 2026
Hitting this constantly on mobile. My setup:
claude-opus-4-6+maxvariantBrowser tab: switching to any other app or locking the phone kills the SSE stream. When I come back, the conversation is frozen — I have to pull-to-refresh or manually reload the page to see the completed response.
PWA: even worse — switching away from the PWA means I have to force-close and reopen it entirely. There's no pull-to-refresh in PWA mode, so the only recovery is killing the app.
This is the single biggest usability pain point on mobile. A reconnect that automatically re-fetches the full session state (like #13947's fix suggested — treating
server.connectedasglobal.disposedto trigger a full refresh) would solve it.IceWreck commentedon Apr 12, 2026
Also facing this all the time.
vitalimi commentedon May 13, 2026
Have same issue:
Opencode serve on VM, using web ui via clodflare/zrok/pinggy.
After sending message i have to manually refresh page to see models answers. After 15 seconds event error
corvofeng commentedon May 21, 2026
Would it be possible to add a refresh button or a better tombstone mechanism? On the web version I can at least manually refresh the page, but as a PWA app I'm stuck staring at a frozen screen with no way to recover.
github-actions commentedon Jul 21, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.