fix: stop waiting on a stuck sandbox create stream - #858
Conversation
📝 WalkthroughWalkthroughThis PR enhances the sandbox creation flow to support early completion when a sandbox becomes ready, regardless of whether the underlying create process has exited. It adds log file isolation for gateway processes and updates test assertions accordingly. Changes
Sequence DiagramsequenceDiagram
participant Client
participant streamSandboxCreate
participant ChildProcess
participant ReadyCheck
Client->>streamSandboxCreate: start with options.readyCheck
streamSandboxCreate->>ChildProcess: spawn create command
ChildProcess-->>streamSandboxCreate: stdout/stderr data
par Polling Loop
streamSandboxCreate->>streamSandboxCreate: setInterval(pollIntervalMs)
loop Every poll interval
streamSandboxCreate->>ReadyCheck: invoke readyCheck()
ReadyCheck-->>streamSandboxCreate: ready = true/false
alt Sandbox ready
streamSandboxCreate->>ChildProcess: send SIGTERM
streamSandboxCreate->>streamSandboxCreate: detachChild()
streamSandboxCreate->>Client: resolve early with forcedReady: true
end
end
and Child Exit Handling
ChildProcess-->>streamSandboxCreate: close event
streamSandboxCreate->>streamSandboxCreate: finish() helper
end
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
bin/lib/onboard.js (1)
259-289: Potential issue:returninsidefinallyblock swallows early returns.In the polling interval callback (lines 263-286), when
options.readyCheck()throws or returns false, the code usesreturnto exit early. However,returnstatements within thetryblock are executed beforefinally, but thefinallyblock runs unconditionally. This works correctly here sincefinallyonly setspolling = false, but thereturnon line 268 and 270 inside thetryblock could be confusing.More importantly, if
readyCheck()throws, line 268 catches it and returns, which is correct. But if other code in thetryblock afterreadyCheck()throws (e.g.,child.killthrows something unexpected that isn't caught), thefinallywill still run and setpolling = false, which is the desired behavior.The logic is sound, but consider adding a brief comment explaining the early-return pattern for future maintainers.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bin/lib/onboard.js` around lines 259 - 289, The polling callback around readyTimer uses early returns inside the try blocks (e.g., after calling options.readyCheck() and when ready is false) which can be confusing to future maintainers; add a concise comment above the inner try/return pattern (referencing the readyTimer callback, options.readyCheck(), polling flag, and the finally that resets polling) explaining that early returns are intentional and that the finally block must always clear polling, so subsequent iterations can proceed; mention that child.kill(), detachChild(), and finish(...) are executed only when ready is true to clarify control flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@bin/lib/onboard.js`:
- Around line 259-289: The polling callback around readyTimer uses early returns
inside the try blocks (e.g., after calling options.readyCheck() and when ready
is false) which can be confusing to future maintainers; add a concise comment
above the inner try/return pattern (referencing the readyTimer callback,
options.readyCheck(), polling flag, and the finally that resets polling)
explaining that early returns are intentional and that the finally block must
always clear polling, so subsequent iterations can proceed; mention that
child.kill(), detachChild(), and finish(...) are executed only when ready is
true to clarify control flow.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b75e6d1e-b7bd-4732-bcce-e00ab50d3daa
📒 Files selected for processing (5)
bin/lib/onboard.jsscripts/nemoclaw-start.shtest/credential-exposure.test.jstest/nemoclaw-start.test.jstest/onboard.test.js
ericksoa
left a comment
There was a problem hiding this comment.
LGTM — nice fix for the stuck create stream. The readiness poll + SIGTERM + detach approach is clean, and the test coverage verifying kill/unref/destroy behavior is thorough.
* fix: detach non-root gateway logs during sandbox startup * fix: stop waiting on a stuck sandbox create stream * fix: detach sandbox create after ready fallback --------- Co-authored-by: Aaron Erickson 🦞 <aerickson@nvidia.com>
* fix: detach non-root gateway logs during sandbox startup * fix: stop waiting on a stuck sandbox create stream * fix: detach sandbox create after ready fallback --------- Co-authored-by: Aaron Erickson 🦞 <aerickson@nvidia.com>
* fix: detach non-root gateway logs during sandbox startup * fix: stop waiting on a stuck sandbox create stream * fix: detach sandbox create after ready fallback --------- Co-authored-by: Aaron Erickson 🦞 <aerickson@nvidia.com>
Summary
openshell sandbox createstream once the sandbox itself is alreadyReadyRoot Cause
After #846, sandbox creation on a Brev CPU Linux box could appear to hang after image upload/import even though the sandbox had already reached
Ready. NemoClaw was waiting for the attachedopenshell sandbox createstream to exit before it ever reached its own readiness polling.Validation
npx vitest run test/onboard.test.js test/onboard-readiness.test.js test/nemoclaw-start.test.js test/credential-exposure.test.jsnemoclaw onboardnow continues past sandbox creation instead of hanging after the image import stepSummary by CodeRabbit
New Features
Tests