Skip to content

[DGX Spark][Policy&Network] recover reports the dashboard forward restored and exits 0 while an unrelated process owns the port #11149

Closed

Description

@wangericnv

Description

When an unrelated local process already listens on a sandbox's dashboard forward port,
nemoclaw {sandbox} recover exits 0 and reports that the host forward was restored and that
launch readiness is healthy. The port is never taken over: the foreign process keeps the
listener for the whole operation, and an HTTP request to the dashboard URL is answered by the
foreign process, not by the sandbox.

The command is therefore telling the user that a forward it did not establish is ready. The
dashboard URL that NemoClaw prints carries an access token in its fragment, so a user who
follows the reported-healthy URL sends that token to whatever local process squatted the port.

Platform scope: Platform-specific not established; reproduced on DGX Spark aarch64 only, other
platforms not tested for this path.
Regression: Unknown - earlier releases were not tested for this path.
OpenShell issue: No. The failing command is NemoClaw's own recover command.

Environment

Device:        NVIDIA DGX Spark
OS:            Ubuntu 24.04.4 LTS
Architecture:  aarch64
Kernel:        6.17.0-1014-nvidia
Node.js:       v22.23.1
npm:           10.9.8
Docker:        Docker version 29.2.1, build a5c7197
OpenShell CLI: openshell 0.0.106
NemoClaw:      nemoclaw v0.0.120
OpenClaw:      2026.7.1

Steps to Reproduce

1. Onboard an OpenClaw sandbox with a dashboard enabled and record its forward port:
     nemoclaw {sandbox} dashboard-url --quiet

2. Stop the sandbox so it releases the forward:
     nemoclaw {sandbox} stop

3. Start an unrelated listener on the freed port and confirm it is serving:
     python3 -m http.server {port} --bind 127.0.0.1 &
     ss -ltnp | grep :{port}
     curl -fsS "http://127.0.0.1:{port}/" -o /tmp/pre.html

4. Ask NemoClaw to recover the sandbox and record the exit code:
     nemoclaw {sandbox} recover; echo "RECOVER_EXIT=$?"

5. Re-check who owns the port and what answers on it:
     ss -ltnp | grep :{port}
     curl -fsS "http://127.0.0.1:{port}/" -o /tmp/post.html

Expected Result

Recovery exits non-zero, names the occupied or unverified-owner port, and withholds launch
readiness. The foreign listener is left running and untouched.

Actual Result

The foreign listener is correctly left untouched, but recovery reports success:

RECOVER_EXIT=0
Probe complete: OpenClaw gateway is running in {sandbox}.
... forwardAction=restored result=ready

Port ownership before and after recovery is identical and belongs to the foreign process:

LISTEN 0 5 127.0.0.1:{port} 0.0.0.0:* users:(("python3",pid={foreign pid},fd=3))

The dashboard request before and after recovery both return the same 9433-byte directory
listing produced by the foreign process, so nothing of the sandbox is reachable on the port
that recovery just declared restored.

Reproduced twice on the same host in independent runs.

Logs

Foreign listener log, showing it served both requests and was never signalled:

  127.0.0.1 - - [07/Sep/2026 06:35:58] "GET / HTTP/1.1" 200 -

Process state after recovery:

  PID STAT CMD
  {foreign pid} S python3 -m http.server {port} --bind 127.0.0.1

Related Bugs / not duplicate of

Same area, different symptom, not a duplicate:

Activity

  1. self-assigned this
    on Sep 8, 2026
  2. Dongni-Yang commented on Sep 8, 2026

    @Dongni-Yang
    Contributor

    Taking this. PR #11196 makes recover name the occupied port, leave the foreign listener untouched, skip the relaunch, and exit 1 with the port in the probe-failure line.

    Context for anyone re-verifying: #11128 (merged today, not linked here) already changed the healthy check to prove listener ownership from the PID, executable and argv, so on main recovery exits 1 in this scenario. It still reported the forward as "missing or dead" and tried to relaunch onto the occupied port, surfacing that launch's "already occupied" second-hand. #11196 completes the expected result stated above and adds a CLI-level test that runs the real ownership proof against a genuine foreign listener.

  3. added
    area: networkingDNS, proxy, TLS, ports, host aliases, or connectivity
    area: sandboxOpenShell sandbox lifecycle, runtime, config, or recovery
    area: securitySecurity controls, permissions, secrets, or hardening
    platform: arm64Affects ARM64 or aarch64 architecture
    platform: dgx-sparkAffects DGX Spark hardware or workflows
    on Sep 8, 2026
  4. added theissue type on Sep 8, 2026
  5. added a commit that references this issue on Sep 12, 2026
    48894b5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area: networkingDNS, proxy, TLS, ports, host aliases, or connectivityarea: sandboxOpenShell sandbox lifecycle, runtime, config, or recoveryarea: securitySecurity controls, permissions, secrets, or hardeningplatform: arm64Affects ARM64 or aarch64 architectureplatform: dgx-sparkAffects DGX Spark hardware or workflows

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions