Skip to content

fix(web): say the host is unreachable and reload when it answers, instead of "Session restore failed" - #29460

Merged
nwparker merged 3 commits into
mainfrom
nwparker/web-offline-startup
Oct 16, 2026
Merged

nwparker merged 3 commits into
mainfrom
nwparker/web-offline-startup

Conversation

@nwparker

@nwparker nwparker commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​131 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​130
Prod 8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​166 $\color{#cf222e}{\Huge{\mathbf{−}}}$​15 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​151

ELI5

The browser version of Orca gets everything from the Orca host it is paired with. If that host wasn't answering when the page loaded (asleep, stopped, or on a changed port), Orca said "Session restore failed" and stopped saving changes until a restart. That sounded like something broke, but nothing had: the host just wasn't answering yet. Now Orca says "Couldn't reach your Orca host. Nothing was lost." and reloads by itself as soon as the host answers. There is also a "Retry now" button.

What Changed

Before: on the paired web client, the "local" catalog is the host. When the host was unreachable at boot, repo.list failed with "Could not connect to the remote Orca runtime". awaitLocalRepoCatalogSettlement then threw, and recoverFromDegradedStartup showed the sticky "Session restore failed / Changes won't be saved until restart" toast. The web transport kept reconnecting in the background, but nothing used that: the page stayed in no-save mode until the user reloaded by hand.

After: recoverFromDegradedStartup first asks isWebHostUnreachableAtStartup(error) (new startup-web-host-unreachable.ts). This is true only when all of these hold:

  • this is the paired web client;
  • the error is a connection failure, using the shared isRecoverableRemoteRuntimeConnectionError plus two web-transport messages it doesn't list: the connection waiter's timeout and "connection interrupted" for a socket that dropped mid-request;
  • the host's transport is still not ready, according to the status owner's snapshot.

In that case it shows an honest offline toast with "Retry now". It also subscribes to runtimeEnvironments.onStatusChanged and reloads once, on the first snapshot whose transport is ready. It re-reads the snapshot once after subscribing, in case the reconnect landed in between. The listener belongs to its startup pass: a pass that was cancelled during the check announces nothing, and aborting the pass removes the listener, so a torn-down StrictMode pass can't reload the page later.

Everything else is unchanged: the shell still mounts with the ready flags forced, and saving stays off until the reload. On desktop, and for real hydration faults (schema, persistence, or a failure on a connected socket), the "Session restore failed" toast and its behavior are exactly as before.

Why

The no-save lock (#1158) protects against writing half-hydrated state. That protection still matters here: the host's projects never loaded, so a session save now would prune the user's tabs. So this PR keeps the lock and changes two things: what the user is told, and how they get out. A reload on the first ready transport gives a clean hydration with no new hydration path to get wrong.

Alternatives considered:

  • Hydrate offline and save normally: that would prune every host-owned tab from the saved session, which is exactly the data loss Fix silent data loss when workspace session hydration fails #1158 prevents.
  • Retry hydration in place without reloading: this re-runs a long chain that mutates state while partial state from the failed run is still around. A reload is simpler and safe, because the web client keeps its state on the host and in local storage.
  • Treat every connection-shaped error as "unreachable": a timeout on a socket that is already ready is a genuine fault, and reloading on the next ready snapshot could loop. So the check also requires a transport that isn't ready.

Loss of contact is reported as just that: the copy says the host didn't answer and that nothing was lost. It never implies the host's work stopped.

Linked Issue

Fixes #26620

Visual Proof

N/A in this PR. The visible change is the toast text, plus an automatic reload when the host comes back. The tests pin both. Copy: "Couldn't reach your Orca host / Nothing was lost. Orca will reload when the host answers, and won't save changes until then. [Retry now]". Strings are added in all six locales.

Testing

  • Automated tests added/updated, or explained why not below

pnpm test src/renderer/src/startup/startup-degraded-recovery.test.ts:

  • New: web client, connection failure, transport down. Shows the unreachable toast, doesn't reload on connecting, and reloads exactly once on ready. Fails on main, which shows "Session restore failed".
  • New: web client, connection-shaped error while the transport is ready. Keeps "Session restore failed" and doesn't arm the reload.
  • New: web client, a failure that isn't about the connection. Keeps "Session restore failed".
  • New: the waiter-timeout and interrupted-socket messages both count as unreachable.
  • New: an aborted startup pass doesn't reload on a later ready, and a pass cancelled during the check neither announces nor subscribes.
  • The existing degraded-recovery tests pass.

pnpm tc:web, pnpm run check:code-quality:changed, pnpm run verify:localization-catalog and verify:localization-extraction pass.

Notes

  • Renderer-only and web-only: desktop is unaffected (isWebClient() is false there).
  • No wire change: it reads only the client's local status snapshots.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • N/A for visual proof with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote impact considered

@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

📝 Walkthrough

Startup recovery now distinguishes unreachable paired web hosts from other hydration failures. It displays a host-specific notice with a manual retry action and relaunches when a ready, non-retired transport is detected. The notice text is available in six locales. Tests cover reconnection, cancellation, connected hosts, and unrelated failures.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: 🔵 Low · up to eae99

A failed partial reconnect can trigger an automatic reload instead of retaining the existing degraded behavior. This is a bounded issue to fix or explicitly accept before merging.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 3 files. (6 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Linked Issues check Passed Issue #26620 requires special handling only for paired web startup connection failures while no active transport is ready. isWebHostUnreachableAtStartup applies those conditions, and `recoverFromDeg…
Out of Scope Changes check Passed The changes stay within Issue #26620. The six locale updates support the new toast, the recovery refactor implements the web-only startup behavior, and the tests verify the requested cases. No unrelat…
Title check Passed The title clearly identifies the main change: report an unreachable web host and reload when the host responds instead of showing the generic session restore failure.
Description check Passed The description is complete and directly matches the changes. It explains the user impact, implementation, rationale, linked issue, visual proof decision, testing, scope, and compatibility considerati…
Full details: Docstring Coverage

Explanation

Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 3 files. (6 skipped: 6 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4a6b3c22-2168-41cd-a37b-cec656516399
📥 Commits

Reviewing files that changed from the base of the PR and between da099c3 and eae9957.

📒 Files selected for processing: 9
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/startup/startup-degraded-recovery.test.ts
  • src/renderer/src/startup/startup-degraded-recovery.ts
  • src/renderer/src/startup/startup-web-host-unreachable.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

if (isCancelled()) {
return
}
if (hostUnreachable) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep partial-reconnect failures on the degraded path.

If reconnectStarted is true and the transport drops during terminal reconnect, this branch announces an unreachable host and arms an automatic reload. The partial-reconnect state is not a clean, unapplied hydration failure. Exclude reconnectStarted from the host-unreachable branch before announcing it. The PR objective requires partial-reconnect failures to retain the existing degraded behavior.

@nwparker
nwparker merged commit 861619f into main Oct 16, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Web client locks into no-save degraded mode when the paired host is unreachable at startup

1 participant