[Ubuntu 26.04][Sandbox] destroying a sandbox on one gateway breaks a sandbox on another gateway
- Dominant language
- TypeScript
- Stars
- 22.5k
- Forks
- 3.1k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 715
Description
## Description
Destroying a sandbox that lives on a non-default gateway breaks a different, still-running sandbox on the default gateway. The destroy changes the host's active-gateway selection even though the surviving sandbox never left the default gateway, and after that the surviving sandbox can no longer be started.
The user sees the damage only when they next start the surviving sandbox. Start exits 1 and reports that no active gateway is set, so the dashboard and API host forward are never created.
Both settings of the flag are affected, so the default path is broken too:
- `--cleanup-gateway` — the active-gateway selection is removed entirely
- `--no-cleanup-gateway` — the active-gateway selection is rewritten to point at the gateway of the sandbox that was just destroyed
Either way the surviving sandbox fails to start. The flag only changes whether the selection is dropped or hijacked.
Two things make this easy to miss. The selection is repaired as a side effect of some ordinary read commands, so running `status` or a connect probe between the destroy and the start hides the failure completely. And the error text tells the user to run the sandbox `recover` command, which does not fix it.
**Platform scope:** Reproduced on Ubuntu 26.04 x86_64 only; other platforms not tested.
**Regression:** Unknown - earlier versions not tested for this path.
## Environment
```text
Device: Ubuntu 26.04 host, x86_64, NVIDIA GeForce RTX 5070 Ti
OS: Ubuntu 26.04 LTS
Architecture: x86_64
Node.js: v22.23.1
npm: 10.9.8
Docker: Docker version 29.6.1, build 8900f1d
OpenShell CLI: openshell 0.0.106 (docker driver)
NemoClaw: nemoclaw v0.0.123
OpenClaw: 2026.7.1
```
## Steps to Reproduce
1. Onboard a sandbox on the default gateway. This one is the survivor and is never touched again except through ordinary commands:
```bash
nemoclaw onboard --name gw-survivor --non-interactive --yes \
--yes-i-accept-third-party-software
```
2. Onboard a second sandbox on its own non-default gateway:
```bash
NEMOCLAW_GATEWAY_PORT=18150 \
NEMOCLAW_OPENSHELL_GATEWAY_STATE_DIR=$HOME/.local/state/nemoclaw/gw18150 \
nemoclaw onboard --name gw-doomed --non-interactive --yes \
--yes-i-accept-third-party-software
```
3. Run any ordinary command against the survivor so the host's active-gateway selection points at the default gateway:
```bash
nemoclaw gw-survivor status
```
4. Destroy the second sandbox:
```bash
nemoclaw gw-doomed destroy --yes --cleanup-gateway
```
5. Stop and start the survivor. Do not run `status` or a connect probe in between, because either one repairs the selection and hides the failure:
```bash
nemoclaw gw-survivor stop
nemoclaw gw-survivor start
```
6. Repeat the whole sequence with `--no-cleanup-gateway` in step 4 to see the other variant.
## Expected Result
Destroying a sandbox on one gateway does not change which gateway the host has selected when that selection belongs to a different gateway that still has a running sandbox.
The surviving sandbox starts normally and its dashboard and API forwards are created.
## Actual Result
Step 4 exits 0 and reports success.
Step 5 fails. Start exits 1:
```text
Container 'openshell-default--gw-survivor-...' started.
Restoring sandbox startup state...
Waiting for sandbox 'gw-survivor' to be ready...
Status: Ready (3s elapsed)
Error: Sandbox 'gw-survivor' started, but startup recovery failed: managed agent gateway
recovery: the recreated sandbox did not become ready in OpenShell. NemoClaw did not start
the primary dashboard/API host forward. Last OpenShell readiness error: Error: No active
gateway. Set one with: openshell gateway. Inspect the current sandbox state before
retrying. Run `nemoclaw gw-survivor recover`, then retry `nemoclaw gw-survivor start`.
```
The dashboard command then fails as well, because the forward was never started:
```text
nemoclaw gw-survivor dashboard-url => exit 1, "Could not retrieve the dashboard auth token"
```
The remedy the error suggests does not work. Running the sandbox `recover` command does not restore the selection. What actually restores it is selecting the gateway again, or running any command that repairs the selection as a side effect.
Controlled comparison, same host, same commands, nothing else changed:
```text
Selection missing => nemoclaw gw-survivor start => exit 1, error above
Selection present again => nemoclaw gw-survivor start => exit 0, dashboard reachable
```
The selection was never edited by hand at any point. It was allowed to be repaired by the product itself, which is what makes the comparison valid.
Both flag settings break the survivor:
```text
--cleanup-gateway selection removed; start exits 1 with the error above
--no-cleanup-gateway selection rewritten to the destroyed sandbox's gateway;
start exits 1 with "Some requested entity was not found"
```
Reproduced twice.
Why this is easy to miss: a plain sandbox `status` call and a connect probe both repair the selection silently as a side effect. If either is run between the destroy and the start, the survivor starts normally and nothing looks wrong. The failure only appears when the user goes straight from destroy to start.
A gateway-scoped read against the destroyed sandbox's own gateway does not change the selection, so the trigger is the lifecycle command, not ordinary reads.
Separate observation from the same runs, recorded so it is not confused with the above: the destroy also stopped the host-shared Ollama auth proxy on loopback port 11435, which left the surviving sandbox's inference returning HTTP 503. That happened with both flag settings, is unrelated to the gateway selection, and recovered on the next connect.
## Logs
```text
Not captured beyond the terminal output quoted in Actual Result.
```
Contributor guide
Research direction
Use the listed nemoclaw reproduction as the first run, then trace the destroy and start lifecycle entry points that update and consume the host active-gateway selection. Verify both cleanup flag variants; done means destroying the non-default sandbox leaves the default selection intact and the survivor starts with its dashboard and API forwards created.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, typescript
- Domain
- cli, infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100