Skip to content

[All Platforms][Uninstall] Confirm prompt self-aborts before user can type #5188

@hunglp6d

Description

@hunglp6d

Agent Diagnostic

Investigated with Claude Code against the repo source:

  • Traced nemoclaw uninstalluninstall.sh (thin wrapper) → dist/nemoclaw.js internal uninstall run-plansrc/lib/actions/uninstall/run-plan.ts.
  • confirm() reads the reply via defaultReadLine(), a byte-by-byte fs.readSync(0, ...) loop whose bare catch returns null on any error.
  • buildRuntime() evaluates !!process.stdin.isTTY before the prompt. The process.stdin getter instantiates the tty stream, and libuv flips fd 0 to non-blocking as a side effect. Verified in-process via /proc/self/fdinfo/0: O_NONBLOCK flips false → true right after touching process.stdin, and the next readSync(0) throws EAGAIN.
  • Reproduced under a pty (script -qec): with process.stdin touched, readSync(0) throws EAGAIN instantly; without the touch it blocks and reads input correctly.
  • Bisected via git log -S "readSync(0": regression introduced by chore(biome): cover and format all TypeScript files #5020, which rewrote defaultReadLine from a child-shell read (sh -c 'IFS= read -r reply', immune because libuv resets child stdio to blocking) to the in-process readSync loop.
  • Ruled out user env: NEMOCLAW_NON_INTERACTIVE=1 / NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1 unset in a fresh shell → identical behavior. confirm() never reads these vars.
  • Existing unit tests inject readLine via deps and CI uses --yes, so the real stdin path was never exercised.

Description

Running nemoclaw uninstall (without --yes) on a Linux terminal prints the banner and Proceed? [y/N], then immediately prints Aborted. and exits — the user never gets a chance to type. Same behavior via bash uninstall.sh. Expected: the prompt waits for y/n.

Root cause: buildRuntime() touches process.stdin (to detect a TTY), which switches fd 0 to non-blocking mode (libuv side effect). The subsequent fs.readSync(0) then throws EAGAIN ("no data yet") because the user hasn't typed in the microseconds after the prompt printed, and the catch-all treats it as EOF → nullAborted.. Deterministic on every Linux TTY.

Regression from #5020 ("chore(biome): cover and format all TypeScript files"), which rewrote defaultReadLine from a child-shell read to the in-process readSync loop. Affects both prompts in the uninstall flow (Proceed? [y/N] and the user-data Also remove them? [y/N]).

Impact: interactive uninstall is unusable on Linux since #5020 merged (2026-06-09). Workaround: nemoclaw uninstall --yes.

Fix PR: #NNNN

Reproduction Steps

  1. Linux machine, real interactive terminal (TTY).
  2. Install any NemoClaw build containing chore(biome): cover and format all TypeScript files #5020 (e.g. v0.0.62-4-gbf817c40d).
  3. Run nemoclaw uninstall (no --yes).
  4. Observe Proceed? [y/N] followed instantly by Aborted. — no keystroke possible. Same with bash uninstall.sh.

Environment

  • OS: DGX OS (Ubuntu-based) — also reproduced on a second Ubuntu workstation
  • Hardware: DGX Spark (spark-b028); not hardware-specific
  • Node.js: v22.22.3
  • Docker: 29.1.3 - not involved (bug is in the CLI prompt layer)
  • NemoClaw: v0.0.62-4-gbf817c40d

Debug Output

Not applicable — the failure is in the CLI confirm-prompt layer, before any sandbox/gateway/Docker interaction. No sandbox is required to reproduce; `nemoclaw debug` bundle contains nothing relevant to this path.

Logs

Terminal transcript (three attempts, all instant-abort):


local-hple@spark-b028:~/NemoClaw$ nemoclaw uninstall
  Running local uninstall script: /localhome/local-hple/NemoClaw/uninstall.sh
NemoClaw Uninstaller
This will remove all NemoClaw resources.
...
Proceed? [y/N]
Aborted.
local-hple@spark-b028:~/NemoClaw$ bash uninstall.sh
...
Proceed? [y/N]
Aborted.
local-hple@spark-b028:~/NemoClaw$ nemoclaw --version
nemoclaw v0.0.62-4-gbf817c40d


Minimal mechanism proof under a pty (input arrives 1 s after read starts):


--- with process.stdin touched (what buildRuntime does) ---
isTTY: true
READ ERR: EAGAIN            <- thrown instantly, misread as EOF
--- without touching process.stdin ---
READ ok: 1 "y"              <- blocks and reads correctly

/proc/self/fdinfo/0: O_NONBLOCK = false -> true after `void process.stdin.isTTY`

Checklist

  • I confirmed this bug is reproducible
  • I searched existing issues and this is not a duplicate

Metadata

Metadata

Assignees

Labels

VRDCIssues and PRs submitted by NVIDIA VRDC test team.area: cliCommand line interface, flags, terminal UX, or outputv0.0.64Release targetv0.0.65Release target

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions