Skip to content

Auto-recover active sessions after signaling disconnects#321

Open
zortos293 wants to merge 2 commits intodevfrom
capy/auto-recover-signaling-discover
Open

Auto-recover active sessions after signaling disconnects#321
zortos293 wants to merge 2 commits intodevfrom
capy/auto-recover-signaling-discover

Conversation

@zortos293
Copy link
Copy Markdown
Collaborator

@zortos293 zortos293 commented Apr 16, 2026

Description

Implement bounded automatic recovery for active sessions when signaling disconnects unexpectedly (e.g., after VPN toggle or network path changes). The renderer now attempts to reclaim and reconnect to the same session instead of immediately tearing down state.

Recovery Logic (src/renderer/src/App.tsx):

  • Attempt recovery only while stream state is in live launch phases (queue, setup, starting, connecting, streaming)
  • Execute up to 2 attempts with delays [0ms, 3000ms] before failing
  • Prefer matching the exact sessionId; fall back to matching the tracked appId if the same session is not found
  • If recovery fails, surface a user-facing launchError and reset to idle state
  • If recovery succeeds, normal offer/WebRTC handling resumes

Explicit Shutdown Guard:

  • Add SignalingRecoveryState ref to track recovery attempts and explicit shutdown intent
  • Call markExplicitSignalingShutdown in handleStopStream and handleDismissLaunchError to prevent automatic recovery after user-initiated stops
  • Reset recovery state on new game launches and navbar resume attempts

Shared Resume Helpers:

  • Extract resolveSessionClaimAppId to safely resolve the appId for session claims
  • Extract applyClaimedSessionAndConnect to centralize session application and signaling connection
  • Refactor claimAndConnectSession to use the extracted helpers

Signaling Event Handler:

  • Replace immediate teardown on disconnected events with a call to attemptSessionRecovery
  • Reset recovery counters on successful offer/streaming transition

Open OPE-063 OPE-063

@zortos293 zortos293 added the capy Generated by capy.ai label Apr 16, 2026 — with Capy AI
@zortos293 zortos293 added the Testing this issue/pr is being tested not ready for official build yet label Apr 16, 2026
@zortos293 zortos293 requested a review from Jayian1890 April 16, 2026 17:55
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
@zortos293
Copy link
Copy Markdown
Collaborator Author

@Jayian1890 if you can test this due to my sub ended so

@Kief5555 Kief5555 requested a review from Copilot April 20, 2026 16:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds bounded automatic recovery in the renderer (App.tsx) to reclaim and reconnect to an existing CloudMatch session after unexpected signaling disconnects (e.g., network/VPN changes), instead of immediately tearing down stream state.

Changes:

  • Introduces SignalingRecoveryState + helpers to track recovery attempts, delays, explicit shutdown intent, and appId context.
  • Implements attemptSessionRecovery flow that queries active sessions, re-claims the best candidate, and reconnects signaling with a limited retry budget.
  • Refactors session resume path by extracting resolveSessionClaimAppId and applyClaimedSessionAndConnect, and updates signaling event handling to use recovery on disconnected.

Comment on lines +2466 to +2472
setStreamStatus("connecting");
signalingRecoveryRef.current.explicitShutdown = false;
await window.openNow.connectSignaling({
sessionId: claimed.sessionId,
signalingServer: claimed.signalingServer,
signalingUrl: claimed.signalingUrl,
});
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applyClaimedSessionAndConnect unconditionally clears signalingRecoveryRef.current.explicitShutdown before connecting signaling. If the user hits Stop/Dismiss while a recovery/claim is already past the earlier explicitShutdown checks (e.g., during getActiveSessions/claimSession), this can re-enable signaling and reconnect against user intent. Consider not mutating explicitShutdown here, and instead re-check explicitShutdown (or a recovery generation/abort token) immediately before applying session state and calling connectSignaling.

Copilot uses AI. Check for mistakes.
Comment on lines +2731 to +2745
} else if (event.type === "disconnected") {
console.warn("Signaling disconnected:", event.reason);
const recovered = await attemptSessionRecovery(event.reason).catch((error) => {
console.error("[Recovery] Signaling recovery failed:", error);
throw error;
});
if (!recovered) {
clientRef.current?.dispose();
clientRef.current = null;
setLaunchError({
stage: streamStatusToLoadingStage(streamStatusRef.current),
title: "Session Connection Lost",
description: "The connection to your running session was lost and could not be restored automatically. Try resuming it again from the app.",
});
resetLaunchRuntime({ keepLaunchError: true, keepStreamingContext: true });
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The disconnected handler treats any attemptSessionRecovery result of false as a hard failure and surfaces a user-facing "Session Connection Lost" error. When attemptSessionRecovery returns false because explicitShutdown was set (user-initiated stop/dismiss) or the status is non-recoverable, this can incorrectly show an error UX for an intentional shutdown. Consider explicitly checking signalingRecoveryRef.current.explicitShutdown (or returning a distinct result) and early-returning without setting launchError in that case.

Copilot uses AI. Check for mistakes.
Comment on lines +1414 to +1417
const markExplicitSignalingShutdown = useCallback((): void => {
signalingRecoveryRef.current.explicitShutdown = true;
signalingRecoveryRef.current.inFlight = null;
}, []);
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

markExplicitSignalingShutdown clears inFlight but does not actually cancel the underlying recovery promise. If a recovery attempt is already running, it can still continue and later call applyClaimedSessionAndConnect, potentially reconnecting after a user stop/dismiss. Consider adding an abort/generation mechanism that the recovery loop (and applyClaimedSessionAndConnect) checks before updating state / reconnecting, rather than only nulling the ref.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

capy Generated by capy.ai Testing this issue/pr is being tested not ready for official build yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants