Skip to content

fix(desktop): forget a Host-removed Session registration on reconnect restore - #4764

Merged
me2seeks merged 1 commit into
apache:mainfrom
me2seeks:fix/desktop-stale-session-observation-reconnect
Sep 4, 2026
Merged

fix(desktop): forget a Host-removed Session registration on reconnect restore#4764
me2seeks merged 1 commit into
apache:mainfrom
me2seeks:fix/desktop-stale-session-observation-reconnect

Conversation

@me2seeks

@me2seeks me2seeks commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Problem

Fixes #4758.

When the desktop main process reconnects to the Runtime Host after an IPC drop, desktop-manager hands the surviving observation registry (target.observations) to each replacement candidate, whose attach() replays every registration through subscription.open. If a previously observed Session no longer exists on the Host (Host restart with ephemeral state, Session GC, deletion by another client), the server deterministically answers subscription.open / not_found ("Session was not found").

The registry's attach() failure cleanup only deleted pending registrations, so a stale active registration stayed in the map forever. The candidate then computed failedSessionIds = observedSessionIds − restoredSessionIds from the pre-attach snapshot, threw Failed to restore Session observations: <id>, and aborted the candidate start. Because the reconnect lifecycle only stops on RuntimeHostPermanentReconnectError, this plain error just fed the backoff loop — every new candidate failed on the same stale registration, and renderer IPC surfaced RuntimeHostHandlerUnavailableError the whole time.

Fix

  • runtime-host-session-observation-registry.ts: recognize RuntimeHostOperationError with operation === "subscription.open" and code === "not_found" as "the Host no longer serves this Session" (isMissingRuntimeHostSessionError). In attach()'s catch path, forget the registration regardless of lifecycle and report it through a new optional onSessionMissing callback. All other failures keep the existing behavior (pending-only cleanup + error reporting), so genuinely transient restore failures still fail the candidate and get retried — see the existing retries candidate startup when a restored observation cannot seed test, which still passes unchanged.
  • runtime-host-desktop-candidate.ts: pass onSessionMissing to emit a deleted sessions-changed event (the renderer already handles deleted safely by refreshing the catalog and retiring the Session view), and compute failedSessionIds from the registrations that survive attach so forgotten Sessions no longer fail the candidate start.

This mirrors the deliberate asymmetry in isRecoverableSubscriptionFailure: session.transcript.page / not_found is transient and retryable, while subscription.open / not_found at attach time is terminal.

Tests

  • New regression test forgets an observed Session the Host no longer serves instead of blocking every reconnect: the second candidate's Host answers subscription.open/not_found for the previously observed Session; the candidate starts (no throw), the registry forgets the stale entry, a deleted change is emitted, and a later candidate can observe new Sessions on the same registry. The test fails without the fix.
  • npm run build:main clean; the 6 runtime-host desktop suites pass 132/132. (Full main-process suite: 2085/2090; the 5 failures are pre-existing browser-message-box environment failures on a display-less Linux box, reproduced on pristine origin/main.)

Real-environment follow-up (not done here): restart a Host / expire a Session with the desktop running and confirm the Failed to restore Session observations loop no longer appears.

… restore

When the Runtime Host no longer serves a previously observed Session
(Host restart with ephemeral state, Session GC, or deletion by another
client), subscription.open deterministically answers not_found during
attach. The observation registry only cleaned up pending registrations
on restore failure, so a stale active registration failed every
candidate start with "Failed to restore Session observations" and the
reconnect lifecycle retried forever while renderer IPC surfaced
RuntimeHostHandlerUnavailableError.

Treat subscription.open/not_found as terminal in attach(): forget the
registration regardless of lifecycle and notify the candidate so it can
emit a deleted sessions-changed event; the renderer already retires the
Session view safely. Compute failed restores from the registrations
that survive attach so forgotten Sessions no longer fail the candidate.

Fixes apache#4758
@github-actions github-actions Bot added the effort/S Under 100 readable lines label Sep 4, 2026

@jackwener jackwener left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Approved. I found no blocking issues.

I verified the production reconnect path: a surviving observation is replayed through subscription.open, and a Host not_found response previously left the active registration behind so every replacement candidate failed again. This patch narrows the terminal case to that exact operation and error code, removes the stale registration, emits the existing deleted session change, and computes restore failures only from registrations that still exist. Other restore failures remain retryable.

The new regression fails when the missing-Session classification is removed and passes with the fix. The affected Desktop suites pass 147/147. I also built the current-main synthetic merge and reran the candidate suite there (22/22); the merge is clean.


Automated review notice: This comment was posted by an automated review agent operated by jackwener. It is not an independent human review and does not replace one.

@me2seeks
me2seeks merged commit bc18454 into apache:main Sep 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/S Under 100 readable lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(desktop): stale active Session observation blocks every Runtime Host reconnect after the Host forgets the Session

2 participants