Skip to content

fix: Gateway keepalive stays off after a failed auto-repair restart - #2111

Merged
shanselman merged 1 commit into
openclaw:mainfrom
shanselman:fix-keepalive-rearm-after-failed-repair
Nov 11, 2026
Merged

shanselman merged 1 commit into
openclaw:mainfrom
shanselman:fix-keepalive-rearm-after-failed-repair

Conversation

@shanselman

Copy link
Copy Markdown
Collaborator

Closes #2092

What Problem This Solves

Fixes: the local WSL Gateway's keepalive stays dead after an auto-repair restart fails, so the distro idles out and restarts uncleanly until something else re-arms it.

User Impact

User impact: when auto-repair force-terminates the Gateway's WSL distro and the restart then fails, the keepalive is now re-armed anyway, so the distro does not drift into repeated unclean stops. Behavior after a successful restart is unchanged. The re-arm is skipped when the repair was cancelled, or the user had already stopped, disconnected, or switched away from the gateway before it began.

Why This Change Was Made

WslManagedLocalGatewayRestarter escalates to WslGatewayController.ForceRestartAsync, which runs wsl.exe --terminate and kills every session of the distro, the keepalive (wsl.exe -d <distro> -- sleep infinity) included. ManagedLocalGatewayRepairCoordinator re-armed the keepalive only on the success path, so a failed or aborted restart after the terminate left the distro without one. In the report that lasted about 6 hours.

The re-arm now runs from the inner finally, while the repair lease is still held (a manual Stop or Switch waits on that lease, so it cannot interleave), and only when a restart was actually attempted. It is skipped if the call was cancelled or GetRestartAbortResult shows the user had already stopped or switched away. That keeps the guard raised in the issue triage: an unconditional re-arm could revive a Gateway the user had just stopped. The re-arm is best effort (an exception is logged and never changes the repair result). WslGatewayKeepAliveService.TryEnsureAsync remains the policy owner and stays idempotent, so it still skips fixture mode, unmanaged gateways, and an already running keepalive.

Known limits: the abort check runs once, before the re-arm starts, and TryEnsureAsync takes no cancellation token, so a Disconnect or shutdown that lands during the re-arm itself is not observed. That is benign: Disconnect never touches WSL, Stop runs inside the distro and leaves the keepalive alone (a keepalive that is up after a Stop is the normal state), and the keepalive is detached on purpose. The old success path had the same window. The re-arm also keeps the lease the success path already held, and TryEnsureAsync inspects WSL processes with its own per-call timeouts, so a manual Stop or Switch queued behind a slow re-arm waits for it. I left both as they are to keep this fix small.

Evidence

The reporter's tray log (issue #2092) shows two wsl.exe --terminate OpenClawGateway calls with no [WslKeepAlive] line until about 6 hours later. The real WSL flow needs a wedged distro to reproduce, so I did not reproduce it live. Instead the coordinator tests drive it with fakes and I mutation-checked them against the production change:

  • Fix reverted: exactly the two FailedRestart_StillRearmsKeepAlive cases fail.
  • Guard removed (unconditional re-arm): exactly the three safeguard tests fail (cancelled, user stop, gateway switch).
  • restartAttempted guard removed: exactly RestartBudget_ExhaustsThenResetAllowsAgain fails.
  • Re-arm not awaited (fire and forget): exactly SlowRearmAfterFailedRestart_KeepsTheLeaseUntilItCompletes fails.
  • Lease released before the re-arm instead of after: the two FailedRestart_StillRearmsKeepAlive cases and the slow-re-arm test fail.

Also covered: the re-arm runs while the lease is still held, a slow re-arm keeps the lease until it completes, and a re-arm that throws after a failed restart keeps the RestartFailed result and still releases the lease. Exact commands and counts are under Validation.

Change Type

  • Bug fix
  • Feature
  • Refactor
  • Docs or instructions
  • Tests or validation
  • Security hardening
  • Chore or infrastructure

Scope

  • Tray or WinUI UX
  • Windows node capability
  • Local MCP or winnode
  • Gateway, connection, or pairing
  • Setup or onboarding
  • Permissions, privacy, or security
  • Tests, CI, or docs

Required proof pools

  • none: no visual, setup, pairing, or host-specific surface changes. The decision is covered by coordinator unit tests with fakes. The real WSL path cannot be reproduced without a wedged distro.

Validation

  • ./build.ps1: exit 0.
  • dotnet test ./tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj --filter "FullyQualifiedName~ManagedLocalGatewayRepairCoordinatorTests|FullyQualifiedName~WslGatewayControllerTests|FullyQualifiedName~WslKeepAlivePolicyTests": 63 passed, 0 failed.
  • dotnet test ./tests/OpenClaw.Shared.Tests/OpenClaw.Shared.Tests.csproj: 5761 passed, 0 failed, 27 skipped.
  • dotnet test ./tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj: 6296 passed, 0 failed.
  • git diff --check: clean. Fresh worktree, so --no-restore was omitted on the first run. OPENCLAW_REPO_ROOT pointed at the worktree and inherited OPENCLAW_* variables were cleared. Before and after file listings of %LOCALAPPDATA%\OpenClawTray and %APPDATA%\OpenClawTray were identical.

Real Behavior Proof

  • Environment tested: Windows ARM64 dev host, .NET 10.0.404, the coordinator's real policy code driven by fake restarter, keepalive, and lease collaborators.
  • PR head or commit tested: this PR's head on top of main 22c111d4.
  • Exact steps or command run: the targeted Tray command under Validation (ManagedLocalGatewayRepairCoordinatorTests, 28 tests).
  • Evidence after fix: a failed restart (restart result failure, and a restarter that throws) now records one keepalive re-arm, made while the lease is still held, and still returns RestartFailed; a slow re-arm keeps the lease until it completes; a re-arm that throws is logged and the lease is still released; user stop, gateway switch, and cancellation record zero re-arms; a restart budget that is exhausted and then reset re-arms only for attempted restarts.
  • Observed result: all targeted and full-suite runs pass (see Validation), and each safeguard test fails when its production guard is removed (see Evidence).
  • Screenshot or artifact links verified? (Yes/No/N/A): N/A, no visual change.
  • Not verified or blocked: a live run against a distro whose restart really fails after wsl.exe --terminate. That needs a wedged distro, so it is covered by the unit tests above and the reporter's log instead.

Security Impact

  • New permissions or capabilities? (Yes/No): No
  • Secrets or tokens handling changed? (Yes/No): No
  • New or changed network calls? (Yes/No): No
  • Command or tool execution surface changed? (Yes/No): No. The same existing keepalive call is made in more cases, still gated by WslKeepAlivePolicy.CanManageGateway.
  • Data access scope changed? (Yes/No): No
  • If any answer is Yes, explain the risk and mitigation: N/A

Compatibility and Migration

  • Backward compatible? (Yes/No): Yes
  • Config or environment changes? (Yes/No): No
  • Migration needed? (Yes/No): No
  • If yes, list the exact upgrade steps: N/A

Review Conversations

  • I replied to or resolved every bot review conversation addressed by this PR.
  • I left unresolved only conversations that still need maintainer judgment.

Prepared by a Copilot agent on behalf of @shanselman during overnight triage; it has not had a human review yet.

Auto-repair of a setup-managed local Gateway force-terminates the WSL
distro before restarting it, and that also kills the keepalive. The
keepalive was re-armed only when the restart succeeded, so a failed
restart left the distro without one until something else asked for it
(about 6 hours in the report).

ManagedLocalGatewayRepairCoordinator now tries to re-arm the keepalive
after any attempted restart, success or failure, while it still holds
the repair lease. It skips the re-arm when the repair was cancelled or
the user had already stopped, disconnected, or switched away from the
gateway before the re-arm began. That check runs once, before the
re-arm; a manual Stop or Switch waits on the lease held across it. The
re-arm is best effort: a failure is logged and never changes the repair
result.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@clawsweeper

clawsweeper Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Nov 11, 2026
@clawsweeper

clawsweeper Bot commented Nov 11, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge.

What this changes

Restore the managed WSL Gateway keepalive after failed restart attempts while preserving cancellation and explicit user intent.

Example: Auto-repair terminates OpenClawGateway, then its cold restart fails.

  • Before: The coordinator returns RestartFailed without restoring the keepalive.
  • After: The coordinator attempts keepalive restoration under the lifecycle lease, unless cancellation or changed user intent prevents it.

Review scores

Measure Result What it means
Overall readiness 🧂 unranked krab (1/6) The implementation is focused and source-correct, but simulated collaborator evidence does not satisfy the changed-runtime proof requirement.
Proof confidence 🧂 unranked krab (1/6) Real behavior proof is not required for maintainer- or bot-authored pull requests.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Product

Kind: Bug fix · Worth it: Yes · Fix scope: Complete
User problem: A failed automatic WSL Gateway restart can leave its distro without keepalive and vulnerable to further idle shutdowns.
Reason: The collaborator-authored direction restores the established supervised-local contract without new settings, APIs, or ownership layers.

Merge readiness

✅ Ready for maintainer review

Keep this PR open: it addresses a source-confirmed recovery defect still present on main, and no introduced correctness defect was identified.

Priority: P1
Reviewed head: 66f9632a2bcedae74c912f17cf42165b238b8d46

Before merge

None.

Findings

None.

Tests

  • Missing end-to-end proof: The supplied evidence does not exercise real WSL termination followed by failed cold restart and observable keepalive restoration.
Agent review details

How this fits together

The tray repair coordinator receives managed-local connection failures, invokes WSL restart operations, restores keepalive through the existing service, and reconnects only after a successful restart.

flowchart TD
  A[Connection failure monitor] --> B[Repair coordinator]
  B --> C[Lifecycle lease and intent checks]
  C --> D[WSL restart adapter]
  D --> E[Keepalive restoration]
  E --> F[Release lifecycle lease]
  F --> G[Return failure or reconnect]
Loading

Technical review

Best possible solution:

Restore keepalive in the existing coordinator's guarded cleanup path while retaining the current service, lifecycle lease, restart budget, and user-intent checks.

Do we have a high-confidence way to reproduce the issue?

Current main establishes termination followed by a fallible cold restart and failure returns that bypass keepalive restoration; no native reproduction ran during this review.

Is this the best way to solve the issue?

The patch is a focused repair using existing owners, and its attempted-restart guard avoids restoration on budget-only exits without adding a competing supervisor.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 22c111d4c209.

Provenance checked

  • Failure-path keepalive restoration changes intended behavior with a stated reason (bc210e0: The original recovery implementation restored keepalive after successful restart while preserving bounded repair and explicit operator intent; no stated reason required skipping restoration after failure.)
  • Gateway identity and user-intent safeguards keeps the original intent (603886a: Recovery handoff hardening added checks that prevent stale gateway or user-intent decisions from continuing restart work.)
  • Documented recovery lifecycle keeps the original intent (01faa64: Managed Gateway recovery documentation specifies serialized, gateway-pinned, intent-aware repair and reconnect.)

Testing

Proof path: unit tests only.

Security

None.

Evidence

What I checked:

Likely related people:

  • Barbara Kudiess: Raw commit 603886a adds src/OpenClaw.Tray.WinUI/Services/ManagedLocalGatewayRepairCoordinator.cs:370 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 603886a81d34; files: src/OpenClaw.Tray.WinUI/Services/ManagedLocalGatewayRepairCoordinator.cs)

Preflight source

Source unknown. Source comparison at the observed PR snapshot only. Completion is self-reported; body, policy, tests, execution, and report bytes are not verified.

Review metrics

Metric Value Why it matters
Change size Production +29/-6; tests +166/-5; documentation +1/-1 Production growth stays within the existing coordinator; test growth protects distinct failure and lifecycle boundaries.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #2092
Summary: This PR directly repairs the failure-path keepalive omission documented by the linked report.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Labels

Label changes:

  • add P1: The source-confirmed failure affects default managed-local recovery and can prolong a real Gateway outage after forced distro termination.
  • add rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Label justifications:

  • P1: The source-confirmed failure affects default managed-local recovery and can prolong a real Gateway outage after forced distro termination.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

Reviewed November 11, 2026, 5:49 AM ET / 10:49 UTC.

@shanselman

shanselman commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator Author

Hi! I'm a Copilot triage agent working overnight for @shanselman (I'm not Scott). I wrote this fix for #2092 (Auto-repair force-terminates the WSL distro but skips the keepalive re-arm when the restart fails): failed recovery can leave keepalive off, and wanted to leave a short status for whoever reviews it.

What was checked on 66f9632a

  • The required CI Gate is green and the merge state is clean.
  • Locally: build.ps1 passed, Tray tests 6296 passed / 0 failed, Shared tests 5761 passed / 0 failed (27 skipped), and the real Companion profile was left unchanged by the test run.
  • The new tests were mutation-checked: five deliberate breakages of the fix were each caught by a test.
  • Two independent AI reviewers read the diff blind. One reviewed the semantics (safe, 90% confidence), the other the tests (safe, 97%). ClawSweeper's review also found no issues.

What is not proven

  • A real WSL distro termination followed by a failed cold restart. The tests use fake restart, keepalive and lease collaborators (ClawSweeper noted the same).
  • The two known limits in the PR body: the abort check runs once before the re-arm starts, so a Disconnect or shutdown that lands during the re-arm itself is not observed (judged benign there, and the old success path had the same window); and a manual Stop or Switch queued behind a slow re-arm waits for the lifecycle lease.

Update at 18:11 UTC: merged the same reviewed head as 65de1e35 after checking the live required CI Gate and review state. Scott explicitly authorized high-confidence self-authored merges in this run. The earlier human-only hold was agent-imposed, not a repository requirement; the recorded proof limits remain unchanged.

@shanselman
shanselman merged commit 65de1e3 into openclaw:main Nov 11, 2026
34 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-repair force-terminates the WSL distro but skips the keepalive re-arm when the restart fails

1 participant