Skip to content

fix(onboard): recover --resume when sandbox container already destroyed#4761

Closed
jason-ma-nv wants to merge 1 commit into
mainfrom
fix/4757-resume-recover-destroyed-container
Closed

fix(onboard): recover --resume when sandbox container already destroyed#4761
jason-ma-nv wants to merge 1 commit into
mainfrom
fix/4757-resume-recover-destroyed-container

Conversation

@jason-ma-nv

@jason-ma-nv jason-ma-nv commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

nemoclaw onboard --resume could not recover a sandbox whose container was already destroyed (e.g. by a non-atomic update/rebuild): the pre-recreate state backup pulls state out of the live container over SSH, so with the container gone the backup returned empty and onboard aborted with State backup failed — aborting recreate to prevent data loss, forcing the operator to discover NEMOCLAW_RECREATE_WITHOUT_BACKUP=1. This teaches the backup step that a confirmed-gone sandbox has nothing to back up, so recovery proceeds automatically.

Related Issue

Fixes #4757

Changes

  • src/lib/onboard/sandbox-backup-on-recreate.ts: add a tri-state containerPresence option to backupSandboxBeforeRecreate; when the container is "absent" it short-circuits with a new failureKind: "no-container" (logs "already gone — nothing to back up; proceeding with recreate") instead of aborting. Add containerPresenceFromReuseState() mapping helper.
  • src/lib/onboard.ts: pass containerPresenceFromReuseState(existingSandboxState) into the pre-recreate backup so a confirmed-missing sandbox skips backup and recreate proceeds.
  • Only a positively-confirmed missing sandbox (OpenShell sandbox get → gRPC NotFound, surfaced by getSandboxStateFromOutputs as "missing") maps to "absent". A gateway transport error or a stale Ready entry stays "unknown" and keeps the fail-safe abort, so a transient gateway blip can never destroy live state.
  • Tests: cover the container-gone short-circuit, the "unknown" fail-safe path, and all three branches of the presence mapping.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Verification notes: This change's own tests pass — src/lib/onboard/sandbox-backup-on-recreate.test.ts (12/12), plus the full onboard/, state/, and rebuild suites green; typecheck:cli clean; Test (plugin) passes. prek run --all-files exited non-zero locally only on pre-existing, environment-specific failures unrelated to this diff: an unbuilt nemoclaw/dist/ (ssrf-parity), oclif test timeouts, a missing /usr/bin/sed on the dev sandbox (fetch-guard), no network egress (e2e curl 403), and the source-shape budget tripping on stray local .claude/worktrees/ checkouts. None touch onboard.ts or the backup path; leaving the two boxes unchecked pending CI on a clean runner.


Signed-off-by: Jason Ma jama@nvidia.com

Summary by CodeRabbit

  • Bug Fixes
    • Sandbox recreation now gracefully handles absent containers by skipping unnecessary backup steps, allowing resume recovery to proceed instead of aborting.
    • Enhanced container presence detection during sandbox state management for more robust recovery scenarios.

…ed (#4757)

A non-atomic update/rebuild can destroy a sandbox's container before
`onboard --resume` runs. The pre-recreate state backup pulls state out of
the live container over SSH, so with the container already gone the backup
returns empty and onboard aborted with "State backup failed — aborting
recreate to prevent data loss", forcing the operator to discover
NEMOCLAW_RECREATE_WITHOUT_BACKUP=1 to recover.

Teach backupSandboxBeforeRecreate a tri-state container presence: when the
sandbox is confirmed gone there is nothing left to back up, so it
short-circuits (failureKind "no-container") and lets recreate proceed.
Presence is derived from the already-computed reuse state — only "missing"
(OpenShell `sandbox get` returns NotFound) is treated as absent; a transport
error or stale-Ready entry stays "unknown" and keeps the fail-safe abort so
a transient gateway blip never destroys live state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Jason Ma <jama@nvidia.com>
@jason-ma-nv jason-ma-nv self-assigned this Jun 4, 2026
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds container presence detection to the sandbox pre-recreate backup flow, allowing onboard --resume to safely skip state backup and proceed when the container is already destroyed, instead of aborting with a data-loss warning.

Changes

Container Presence & Backup Skip for Recreate

Layer / File(s) Summary
Container Presence Type & Detection Function
src/lib/onboard/sandbox-backup-on-recreate.ts, src/lib/onboard/sandbox-backup-on-recreate.test.ts
Introduces SandboxContainerPresence type with three states (present, absent, unknown), extends PreRecreateBackupOptions with optional containerPresence field, adds "no-container" failure kind, and implements containerPresenceFromReuseState to map reuse-state strings to presence values. Tests verify mapping behavior for all three states.
Backup Function Update to Skip Absent Containers
src/lib/onboard/sandbox-backup-on-recreate.ts, src/lib/onboard/sandbox-backup-on-recreate.test.ts
Modifies backupSandboxBeforeRecreate to detect when containerPresence is "absent", log that the container is already gone, and return early with success and no backup without invoking the backup implementation. Tests verify absent-presence skips backup and unknown-presence still aborts.
Onboard Recreation Flow Integration
src/lib/onboard.ts
Imports containerPresenceFromReuseState and updates the createSandbox recreation logic to compute containerPresence from existingSandboxState and pass it to backupSandboxBeforeRecreate, enabling backup skip when the container is already destroyed.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

fix, onboarding

Suggested reviewers

  • cv
  • prekshivyas
  • cjagwani

Poem

🐰 A container gone, but state lives on—
No backup needed when the dance is done!
Skip the futile reach, let --resume flow,
Sandbox reborn from what was left below. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: enabling --resume recovery when the sandbox container is already destroyed, which directly addresses the primary changeset objective.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from #4757: tri-state containerPresence handling with short-circuit logic for 'absent', fail-safe abort for 'unknown', presence mapping from reuse state, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issue objectives: containerPresence handling, short-circuit backup logic, presence mapping, and test coverage; no extraneous modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/4757-resume-recover-destroyed-container

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

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: onboard-resume-e2e, rebuild-openclaw-e2e
Optional E2E: upgrade-stale-sandbox-e2e, state-backup-restore-e2e

Dispatch hint: onboard-resume-e2e,rebuild-openclaw-e2e

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • onboard-resume-e2e (medium; live Docker/OpenShell sandbox, 60 minute timeout, requires NVIDIA_API_KEY): Directly exercises interrupted onboarding followed by nemoclaw onboard --resume, the user flow most closely related to the changed resume/recreate recovery logic in src/lib/onboard.ts.
  • rebuild-openclaw-e2e (medium-high; builds an old base image and rebuilds a live sandbox, 60 minute timeout, requires NVIDIA_API_KEY): Exercises sandbox rebuild through the recreate path while verifying workspace state survives and no credentials leak. This is the strongest existing coverage for pre-recreate backup behavior after the backup decision logic changed.

Optional E2E

  • upgrade-stale-sandbox-e2e (medium-high; old-image build plus rebuild, 60 minute timeout, requires NVIDIA_API_KEY): Adjacent stale-sandbox upgrade/rebuild flow that can enter similar recreate logic after detecting an older OpenClaw sandbox. Useful confidence for upgrade recovery but less targeted than rebuild-openclaw-e2e.
  • state-backup-restore-e2e (medium; live sandbox backup/restore, 60 minute timeout, requires NVIDIA_API_KEY): Validates live workspace backup/restore primitives used for sandbox state preservation. It does not specifically cover onboard pre-recreate skip-on-missing-container behavior, so it is optional adjacent coverage.

New E2E recommendations

  • onboard resume after missing sandbox/container (high): Existing E2E covers resume, rebuild, and normal state-preserving backup, but none appears to reproduce issue [Linux][Upgrade] nemoclaw onboard --resume aborts with "State backup failed" — cannot back up from an already-destroyed sandbox container #4757 exactly: registry/session says a sandbox should be recreated, OpenShell reports it as missing, and onboard/rebuild must skip pre-recreate backup instead of aborting with the empty-backup data-loss warning.
    • Suggested test: Add an E2E that onboards or seeds a resumable sandbox state, deletes the underlying OpenShell sandbox/container before nemoclaw onboard --resume or rebuild, then asserts recreate proceeds, no backup abort warning is emitted, and the sandbox returns to Ready. Include a negative/assertion phase where presence is unknown/not_ready and empty backup still aborts fail-safe.

Dispatch hint

  • Workflow: .github/workflows/nightly-e2e.yaml
  • jobs input: onboard-resume-e2e,rebuild-openclaw-e2e

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

E2E Scenario Advisor Recommendation

Required scenario E2E: ubuntu-repo-cloud-openclaw-resume
Optional scenario E2E: ubuntu-repo-cloud-openclaw-repair

Dispatch required scenario E2E:

  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-resume

Workflow run

Full scenario advisor summary

E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required scenario E2E

  • ubuntu-repo-cloud-openclaw-resume: The PR changes onboarding recreate/resume backup behavior, specifically allowing recreate to proceed when the sandbox container is confirmed absent. The resume-after-interrupt OpenClaw scenario is the smallest routed scenario that targets the affected onboarding recovery lifecycle on the standard Ubuntu repo/Docker path.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-resume

Optional scenario E2E

  • ubuntu-repo-cloud-openclaw-repair: Adjacent lifecycle coverage for repairing an existing OpenClaw configuration; useful if reviewers want broader confidence around recreate/recovery flows, but not the primary target of the absent-container resume fix.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw-repair

Relevant changed files

  • src/lib/onboard.ts
  • src/lib/onboard/sandbox-backup-on-recreate.ts

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/onboard.ts`:
- Around line 3242-3248: The added explanatory comment increases file length;
keep behavior but shrink lines by moving or condensing the rationale: replace
the multi-line comment above the backupSandboxBeforeRecreate call with a single
short inline comment (e.g., "// Skip backup if sandbox already removed") and/or
move the detailed rationale into the onboarding backup module or tests; leave
the call to backupSandboxBeforeRecreate({ sandboxName, containerPresence:
containerPresenceFromReuseState(existingSandboxState) }) and the
existingSandboxState/sandboxName references unchanged so functionality is
preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 1c0389c9-2c87-406d-8f98-c6462091eecd

📥 Commits

Reviewing files that changed from the base of the PR and between 17734b1 and 722a2a0.

📒 Files selected for processing (3)
  • src/lib/onboard.ts
  • src/lib/onboard/sandbox-backup-on-recreate.test.ts
  • src/lib/onboard/sandbox-backup-on-recreate.ts

Comment thread src/lib/onboard.ts
Comment on lines +3242 to +3248
// #4757: if the sandbox is already gone (e.g. a non-atomic update
// destroyed the container before `onboard --resume` ran), there is
// nothing to back up — skip rather than abort so recovery can proceed.
const result = backupSandboxBeforeRecreate({
sandboxName,
containerPresence: containerPresenceFromReuseState(existingSandboxState),
});

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reduce net line growth in src/lib/onboard.ts to unblock CI.

This change is functionally good, but CI is currently blocked by the codebase growth guardrail on this file (+7 net lines). Please keep the behavior and trim/move lines (e.g., condense the inline rationale or move extra rationale to the onboarding backup module/tests) so this file no longer grows.

Minimal trim suggestion in this hunk
-      // `#4757`: if the sandbox is already gone (e.g. a non-atomic update
-      // destroyed the container before `onboard --resume` ran), there is
-      // nothing to back up — skip rather than abort so recovery can proceed.
       const result = backupSandboxBeforeRecreate({
         sandboxName,
-        containerPresence: containerPresenceFromReuseState(existingSandboxState),
+        containerPresence: containerPresenceFromReuseState(existingSandboxState), // `#4757`: confirmed-missing sandbox skips backup
       });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard.ts` around lines 3242 - 3248, The added explanatory comment
increases file length; keep behavior but shrink lines by moving or condensing
the rationale: replace the multi-line comment above the
backupSandboxBeforeRecreate call with a single short inline comment (e.g., "//
Skip backup if sandbox already removed") and/or move the detailed rationale into
the onboarding backup module or tests; leave the call to
backupSandboxBeforeRecreate({ sandboxName, containerPresence:
containerPresenceFromReuseState(existingSandboxState) }) and the
existingSandboxState/sandboxName references unchanged so functionality is
preserved.

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor

Findings: 1 needs attention, 1 worth checking, 0 nice ideas
Since last review: 0 prior items resolved, 0 still apply, 1 new item found

Review findings

🛠️ Needs attention

  • Destroyed-container resume path still needs source-level coverage (src/lib/onboard/sandbox-backup-on-recreate.ts:106): The linked issue explicitly covers a sandbox whose container is destroyed while state/registry metadata remains, including the manual `docker rm -f <sandbox-container>` repro. This patch only maps `getSandboxReuseState() === "missing"` to `containerPresence: "absent"`; `"not_ready"` and stale metadata states remain `"unknown"` and still run the SSH backup path that can abort with an empty backup. The added tests exercise the low-level helper with a pre-supplied `containerPresence: "absent"`, but they do not prove that `nemoclaw onboard --resume` reaches that state for the reported stale-entry/container-gone condition or preserves the registry/session data needed for recreate.
    • Recommendation: Add a targeted mocked integration test around the resume/recreate boundary for a completed resumed sandbox with registry/session state present and the backing container gone. If `sandbox get`/`sandbox list` reports stale metadata or `not_ready` for that case, add a source-level presence probe that can distinguish confirmed container absence from transient gateway errors before skipping backup; otherwise document and test why the reported condition always produces `"missing"`.
    • Evidence: Issue step 2 says: `Drive the sandbox into the "container destroyed but state/registry entry remains" condition ... OR manually: docker rm -f <sandbox-container>`. The new mapper is `if (reuseState === "missing") return "absent"; if (reuseState === "ready") return "present"; return "unknown";`, and the `unknown` test asserts the empty backup still aborts.

🔎 Worth checking

  • Source-of-truth review needed: Pre-recreate backup skip for destroyed sandbox containers: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: Localized recovery comments appear in `src/lib/onboard.ts` near the backup call and in `src/lib/onboard/sandbox-backup-on-recreate.ts`; the mapper only treats reuse state `"missing"` as absent.

🌱 Nice ideas

  • None.
Since last review details

Current findings:

  • Source-of-truth review needed: Pre-recreate backup skip for destroyed sandbox containers: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: Localized recovery comments appear in `src/lib/onboard.ts` near the backup call and in `src/lib/onboard/sandbox-backup-on-recreate.ts`; the mapper only treats reuse state `"missing"` as absent.
  • Destroyed-container resume path still needs source-level coverage (src/lib/onboard/sandbox-backup-on-recreate.ts:106): The linked issue explicitly covers a sandbox whose container is destroyed while state/registry metadata remains, including the manual `docker rm -f <sandbox-container>` repro. This patch only maps `getSandboxReuseState() === "missing"` to `containerPresence: "absent"`; `"not_ready"` and stale metadata states remain `"unknown"` and still run the SSH backup path that can abort with an empty backup. The added tests exercise the low-level helper with a pre-supplied `containerPresence: "absent"`, but they do not prove that `nemoclaw onboard --resume` reaches that state for the reported stale-entry/container-gone condition or preserves the registry/session data needed for recreate.
    • Recommendation: Add a targeted mocked integration test around the resume/recreate boundary for a completed resumed sandbox with registry/session state present and the backing container gone. If `sandbox get`/`sandbox list` reports stale metadata or `not_ready` for that case, add a source-level presence probe that can distinguish confirmed container absence from transient gateway errors before skipping backup; otherwise document and test why the reported condition always produces `"missing"`.
    • Evidence: Issue step 2 says: `Drive the sandbox into the "container destroyed but state/registry entry remains" condition ... OR manually: docker rm -f <sandbox-container>`. The new mapper is `if (reuseState === "missing") return "absent"; if (reuseState === "ready") return "present"; return "unknown";`, and the `unknown` test asserts the empty backup still aborts.

Workflow run details

This is an automated advisory review. A human maintainer must make the final merge decision.

@jason-ma-nv
jason-ma-nv marked this pull request as draft June 4, 2026 07:22
@copy-pr-bot

copy-pr-bot Bot commented Jun 4, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@wscurran wscurran added area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression labels Jun 5, 2026
@wscurran

wscurran commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this detailed PR about fixing the onboard --resume issue when a sandbox container is already destroyed, which improves the robustness of the recreate process. This proposes a bug fix for the onboard flow that affects sandbox management and error handling.


Related open issues:

@jason-ma-nv

Copy link
Copy Markdown
Collaborator Author

Closing — the target issue #4757 is already resolved.

#4757 was closed as completed: QA re-verified on NemoClaw v0.1.0 (DGX Spark) that the recovery flow was redesigned upstream — the chicken-and-egg "State backup failed" dead-end is gone and the recreate command (nemoclaw onboard --recreate-sandbox) is now surfaced inline rather than via the hidden NEMOCLAW_RECREATE_WITHOUT_BACKUP=1 env var.

Since the underlying problem this PR targeted is fixed in the latest release by a different (broader) redesign, this change is superseded and no longer needed. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: sandbox OpenShell sandbox lifecycle, runtime, config, or recovery bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Linux][Upgrade] nemoclaw onboard --resume aborts with "State backup failed" — cannot back up from an already-destroyed sandbox container

2 participants