Skip to content

fix(onboard): short-circuit gateway start when Docker daemon is unreachable (#2347) - #2386

Closed
chengjiew wants to merge 1 commit into
mainfrom
fix/2347-onboard-docker-hang
Closed

fix(onboard): short-circuit gateway start when Docker daemon is unreachable (#2347)#2386
chengjiew wants to merge 1 commit into
mainfrom
fix/2347-onboard-docker-hang

Conversation

@chengjiew

@chengjiew chengjiew commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Summary

When Colima/Docker is stopped on macOS, nemoclaw onboard failed at [2/8] Starting OpenShell gateway with Socket not found: /var/run/docker.sock and then kept retrying the health poll for up to ~15 minutes (3 attempts × ~300s ARM64 health-wait) before surfacing a generic openshell doctor logs troubleshooting message. The issue author reported this as an indefinite hang (against v0.0.23, which predated the existing NEMOCLAW_GATEWAY_START_TIMEOUT bound added in #1830).

This PR makes that failure mode fast-fail with an actionable error instead of burning ~15 minutes:

  • Adds a pure classifyGatewayStartFailure(output) helper in src/lib/validation.ts that recognizes the Socket not found: /var/run/docker.sock (macOS Colima stopped) and Cannot connect to the Docker daemon (Linux dockerd stopped) signatures emitted by openshell gateway start.
  • In startGatewayWithOptions, when openshell gateway start exits non-zero AND the output classifies as docker_unreachable, aborts the retry loop via pRetry.AbortError — no further health polls, no further retries.
  • In the onboard failure branch, prints a short platform-specific remediation (colima start on macOS, sudo systemctl start docker on Linux) in place of the openshell troubleshooting dump.

Picks option (b) from the issue — does not auto-invoke colima start or systemctl start docker.

Relationship to #2348 / PR #2372

PR #2372 (for sibling issue #2348) adds the preflight detection so onboard fast-fails at step [1/8] when Docker is already stopped before onboard runs. That PR is the primary defense; this PR is the defense-in-depth for the case where the daemon dies mid-onboard or preflight is bypassed. Together they cover both halves of what the #2347 issue asks for.

The helper name (classifyGatewayStartFailure) parallels the existing classifySandboxCreateFailure and classifyValidationFailure conventions in src/lib/validation.ts.

Behavior unchanged on the success path

The classifier is only consulted when startResult.status !== 0. When openshell gateway start succeeds, or when it exits non-zero for reasons other than the Docker socket (e.g. slow k3s bootstrap, image pull), the retry + health-poll behavior is identical to before.

Testing

  • Added 6 unit tests for classifyGatewayStartFailure:
    • macOS Colima-stopped signature (Socket not found: /var/run/docker.sock)
    • Linux dockerd-stopped signature (Cannot connect to the Docker daemon at unix:///var/run/docker.sock)
    • Bare Failed to create Docker client marker
    • Free-form docker daemon is not running wording
    • Slow-bootstrap output (Helm chart apply, pod startup duration) returns unknown so the retry loop stays engaged — regression guard
    • Empty / missing output → unknown
  • npx vitest run src/lib/validation.test.ts → 48/48 passing
  • npx vitest run test/gateway-start-wait.test.ts test/onboard.test.ts test/onboard-readiness.test.ts → 176/176 passing
  • npx tsc -p tsconfig.src.json / npx tsc -p tsconfig.cli.json clean
  • Live macOS repro (colima stopnemoclaw onboard): not run. The change is exercised by the unit tests on the pure classifier + is a surgical edit to a well-tested code path.

Diff size

3 files changed, 119 insertions(+), 0 deletions(-).

Fixes #2347

Signed-off-by: Chengjie Wang chengjiew@nvidia.com

Summary by CodeRabbit

  • New Features

    • Enhanced gateway startup error detection to identify Docker daemon connectivity issues and provide targeted OS-specific instructions for resolution, streamlining troubleshooting.
  • Tests

    • Added test coverage for Docker daemon failure classification across multiple error scenarios.

…chable (#2347)

When Colima/Docker is stopped, `openshell gateway start` prints
"Failed to create Docker client. Socket not found: /var/run/docker.sock"
and exits non-zero, but onboard then kept polling the gateway for health
for up to ~15 minutes (3 attempts × ~300s ARM64 health wait) before
surfacing a generic "openshell doctor logs" message.

Add `classifyGatewayStartFailure(output)` so onboard recognizes the
docker-daemon-down signatures (macOS Colima stopped and Linux
Cannot-connect-to-daemon) and aborts the retry loop via
`pRetry.AbortError` instead. On abort, print an actionable
platform-specific remediation (`colima start` on macOS,
`sudo systemctl start docker` on Linux) in place of the openshell
troubleshooting dump, so the user can recover immediately.

Complements the preflight detection in PR #2372 (#2348) — that PR
blocks the issue at step [1/8] when Docker is already down before
onboard runs; this change handles the same failure if the daemon dies
mid-onboard or preflight is skipped, and replaces a 15-minute bounded
hang with an immediate, clear error.

Signed-off-by: Chengjie Wang <chengjiew@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Apr 23, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Implementation of Docker daemon detection during gateway startup failures. The code classifies gateway startup errors as "docker_unreachable" or "unknown", aborts retries when Docker is unreachable, and displays platform-specific Docker restart instructions upon final failure.

Changes

Cohort / File(s) Summary
Validation Framework
src/lib/validation.ts
New GatewayStartFailure interface and classifyGatewayStartFailure() function that detects Docker daemon reachability issues from gateway output, recognizing macOS socket errors, Linux daemon connection failures, and Docker client creation errors.
Validation Tests
src/lib/validation.test.ts
Test suite verifying classifier correctly identifies Docker-unreachable error signatures (missing sockets, daemon connection failures, client creation errors) and returns unknown for ambiguous or healthy-looking startup logs.
Onboard Integration
src/lib/onboard.ts
Imports classifier function, integrates into retry loop to detect unrecoverable Docker failures, aborts retries immediately when Docker is unreachable, and prints OS-specific Docker startup commands on final failure instead of generic logs.

Sequence Diagram

sequenceDiagram
    actor User
    participant Onboard
    participant Classifier
    participant Gateway
    participant Docker
    
    User->>Onboard: Start onboard
    Onboard->>Gateway: Attempt gateway start
    Gateway->>Docker: Connect to daemon
    Docker-->>Gateway: Connection failed (daemon unreachable)
    Gateway-->>Onboard: Return error output
    Onboard->>Classifier: Classify failure type
    Classifier-->>Onboard: { kind: "docker_unreachable" }
    Onboard->>Onboard: Mark as unrecoverable
    Onboard->>Onboard: Abort p-retry loop
    Onboard->>User: Print OS-specific Docker restart command
    Onboard->>Onboard: Exit process
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A daemon that slumbers, a socket that hides—
The gateway would fail and the onboard gets stuck!
But now we detect when the Docker won't start,
We classify failures with precision and heart,
And tell the kind user exactly what's needed—
Colima or systemctl, their plea shall be heeded! 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly addresses the main change: short-circuiting gateway start when Docker daemon is unreachable, which is the primary objective of the PR.
Linked Issues check ✅ Passed The code changes fully implement the issue requirements: detects Docker unreachability via classifyGatewayStartFailure, aborts retries with AbortError, and prints OS-specific remediation messages (option b from issue #2347).
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #2347: validation.ts adds the classifier, onboard.ts integrates Docker detection logic, and validation.test.ts provides comprehensive test coverage.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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/2347-onboard-docker-hang

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

@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.

🧹 Nitpick comments (1)
src/lib/onboard.ts (1)

6848-6848: Optional: keep onboard exports narrowly scoped.

If no external consumer needs this symbol from onboard, consider not re-exporting classifyGatewayStartFailure and keeping it sourced from validation only.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/onboard.ts` at line 6848, The export list in onboard.ts re-exports
classifyGatewayStartFailure but it appears unused externally; remove
classifyGatewayStartFailure from onboard's public exports and leave its
import/definition in validation so external callers import it from validation
instead; locate the export array or export block in onboard.ts (where
classifyGatewayStartFailure currently appears) and delete that symbol, then run
a project-wide search for classifyGatewayStartFailure to ensure all internal
uses still import it from validation and update any import sites if necessary.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/lib/onboard.ts`:
- Line 6848: The export list in onboard.ts re-exports
classifyGatewayStartFailure but it appears unused externally; remove
classifyGatewayStartFailure from onboard's public exports and leave its
import/definition in validation so external callers import it from validation
instead; locate the export array or export block in onboard.ts (where
classifyGatewayStartFailure currently appears) and delete that symbol, then run
a project-wide search for classifyGatewayStartFailure to ensure all internal
uses still import it from validation and update any import sites if necessary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: bd4f6ec1-f5c1-4629-be48-1082ea7e1a9c

📥 Commits

Reviewing files that changed from the base of the PR and between 5b1082b and 16d7d02.

📒 Files selected for processing (3)
  • src/lib/onboard.ts
  • src/lib/validation.test.ts
  • src/lib/validation.ts

@ericksoa ericksoa 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.

Clean, well-scoped fix. Turns a 15-minute bounded hang into an immediate actionable error.

Looks good

  • Pure classifier in validation.ts — follows the existing classifySandboxCreateFailure / classifyValidationFailure pattern. Four regex patterns cover the known Docker-down signatures across macOS (Colima) and Linux (dockerd).
  • Short-circuit via pRetry.AbortError — correct mechanism to bail out of the retry loop without further health polls.
  • Platform-specific remediationcolima start on darwin, systemctl start docker on linux, generic fallback otherwise.
  • No impact on success path — classifier only consulted when startResult.status !== 0.
  • Regression guard — the "slow bootstrap" test case ensures normal k3s startup output (HelmChart, pod startup duration) is classified as unknown, keeping the retry loop engaged for legitimate slow starts.
  • Good relationship with #2372 — this is defense-in-depth for daemon death mid-onboard; #2372 covers preflight. Together they close both halves of #2347.

Same note as #2356: the commit is authored by Test User <test@example.com> — git config needs fixing for future PRs. Not blocking since squash-merge uses the PR author identity.

LGTM.

@ericksoa ericksoa 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.

Revising my earlier approval based on deeper analysis of the classifier.

Blocking: classifyGatewayStartFailure over-matches on "Failed to create Docker client"

The regex /Failed to create Docker client/i matches anywhere in the output, including historical or explanatory text. For example, output like:

client created successfully; previous Failed to create Docker client issue fixed

...would trigger docker_unreachable and abort retries with misleading Docker remediation, even though the daemon is fine. This converts a previously retryable gateway-start failure into an immediate hard failure — contradicting the PR's claim that non-Docker failure behavior is unchanged.

Recommended fix

Tighten the matcher to require stronger surrounding error context. For example, anchor the "Failed to create Docker client" pattern to line-start or require it to co-occur with a socket/daemon keyword:

/^\s*(?:Error:\s*)?Failed to create Docker client/im

Or require the "Socket not found" / "Cannot connect" patterns as the primary matchers and drop the broad "Failed to create Docker client" standalone match entirely — the first two patterns already cover the known macOS and Linux signatures.

Recommended test coverage

Add a regression test for classifyGatewayStartFailure with explanatory/history text containing "Failed to create Docker client" in a non-error context, asserting it returns unknown (not docker_unreachable).

Also add

An integration/regression test around startGatewayWithOptions that stubs gateway-start output to docker-unreachable text and asserts zero health polls plus the Docker-specific stderr output.

What still looks good

  • The architecture (pure classifier + pRetry.AbortError short-circuit) is correct.
  • The platform-specific remediation UX is good.
  • The slow-bootstrap regression guard test is the right idea — just needs a false-positive case too.
  • The relationship with #2372 (preflight) is well-scoped.

@wscurran wscurran added bug platform: macos Affects macOS, including Apple Silicon labels Apr 27, 2026
@wscurran

wscurran commented May 6, 2026

Copy link
Copy Markdown
Contributor

Thanks for this. Since #2006 has merged in the same gateway-start path, could you please rebase this PR on current main and confirm the Docker-unreachable fast-fail behavior is still needed for #2347? Also please address the existing review feedback so we can reassess this as a targeted follow-up rather than treating it as superseded.

@cv cv closed this May 12, 2026
@cv cv reopened this May 12, 2026
@wscurran

Copy link
Copy Markdown
Contributor

Closing due to inactivity. This PR needs to be rebased against current main and has unresolved review feedback; review has been blocked for 7+ days without an update.

The linked issue #2347 remains open. Feel free to reopen this PR once it is rebased and the Docker-unreachable fast-fail behavior is confirmed still needed, or open a fresh PR with the targeted follow-up. Thanks for contributing!

@wscurran wscurran closed this May 19, 2026
@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output area: packaging Packages, images, registries, installers, or distribution bug-fix PR fixes a bug or regression platform: container Affects Docker, containerd, Podman, or images needs: rebase PR needs rebase or conflict resolution and removed area: packaging Packages, images, registries, installers, or distribution priority: medium labels Jun 3, 2026
cv added a commit that referenced this pull request Jun 3, 2026
…rt (#4128)

## Summary

Follow-up to closed PR #2386 for #2347, rebased onto current `main`.

This keeps the #2347 fix targeted to gateway startup failures where
Docker/Colima is unreachable:

- classify Docker-daemon-down `openshell gateway start` output and abort
the retry/health-poll loop immediately
- print platform-specific recovery guidance (`colima start` / `sudo
systemctl start docker`) instead of waiting several minutes and ending
with generic gateway diagnostics
- cover both the legacy `openshell gateway start` path and the current
Docker-driver gateway failure reporter
- address previous review feedback by anchoring the `Failed to create
Docker client` matcher so historical/explanatory text is not
misclassified
- remove the unnecessary `onboard.ts` re-export of
`classifyGatewayStartFailure`
- keep new gateway-failure handling in `src/lib/onboard/` so the
top-level `src/lib/onboard.ts` entrypoint stays net-neutral

Fixes #2347.

## Verification

- `npm run build:cli`
- `npx vitest run src/lib/validation.test.ts
src/lib/onboard/docker-driver-gateway-failure.test.ts
test/onboard.test.ts test/gateway-final-failure-cleanup.test.ts`

Note: the normal pre-push hook reached the full CLI coverage step and
appeared to hang in `vitest --coverage` with 0% CPU after the earlier
hook checks and TypeScript CLI passed, so the branch push was completed
with `--no-verify` after the focused build/test verification above.

Signed-off-by: Chengjie Wang <chengjiew@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved detection of Docker daemon connectivity failures during
gateway startup, enabling faster failure reporting without health
polling delays.

* **New Features**
* Added platform-specific recovery guidance (Docker Desktop, Colima, or
systemd) when Docker daemon is unreachable.

* **Tests**
* Added test coverage for Docker unreachable scenarios and recovery
messaging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Chengjie Wang <chengjiew@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
@wscurran wscurran removed the bug label Jun 8, 2026
@cv
cv deleted the fix/2347-onboard-docker-hang branch June 28, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression needs: rebase PR needs rebase or conflict resolution platform: container Affects Docker, containerd, Podman, or images platform: macos Affects macOS, including Apple Silicon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NemoClaw][macOS][Onboard] nemoclaw onboard does not auto-restart Docker (Colima) when daemon is stopped — hangs indefinitely at gateway health

4 participants