fix(desktop): Finder launch crashed with 'spawn node ENOENT' on nvm-only machines - #290
Open
dapsychyoo wants to merge 2 commits into
Open
fix(desktop): Finder launch crashed with 'spawn node ENOENT' on nvm-only machines#290dapsychyoo wants to merge 2 commits into
dapsychyoo wants to merge 2 commits into
Conversation
A Finder launch crashed with 'Uncaught Exception: spawn node ENOENT' on machines where node is installed only via nvm. The login-shell fallback took the LAST line of stdout from `$SHELL -lic 'command -v node'`, but rc files freely print to stdout — iTerm2 shell integration emits OSC 1337 escape sequences without a trailing newline, so the node path came back glued to escape garbage, existsSync() rejected it, and the app fell through to bare 'node', which the stripped GUI PATH cannot find. - Extract resolution into desktop/node-resolve.js (unit-testable; main.js requires electron) and probe the login shell with the same \x01-sentinel technique src/shell-path.js already uses, immune to rc noise. - Scan ~/.nvm/versions/node/*/bin/node (highest first) before spawning a shell at all — fixes nvm-only machines even when the probe fails, and skips the ~1s shell spawn on them. - Handle spawn 'error' in startServer: a clear dialog (install Node 18+ or set CODBASH_NODE) instead of an uncaught exception. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3q8EdVFkKPTtSGhAU1Wxf
…ult, guard dialogs - Drop the duplicated sentinel probe: reuse src/shell-path.js's cached, logged captureLoginShellPath() (dev: ../src, packaged: extraResources app/src) and scan its PATH for node. - listNvmNodes: put ~/.nvm/alias/default (exact or prefix form) first so the scan never silently overrides the user's default with a newer install (prebuilt node-pty ABI may not match); drop the double stat — the candidate loop owns the existence check via the injected seam. - startServer 'error' handler: return early during a quit in progress; in SMOKE mode fail loudly with a nonzero exit instead of a silent green run. - whenReady catch: skip the generic 'failed to start' dialog when the spawn error dialog already told the user what's wrong. - Tests: scope injected existsSync fakes to the fake home so the suite doesn't depend on the host machine's /usr/local/bin/node (CI runners all have one). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3q8EdVFkKPTtSGhAU1Wxf
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Launching codbash.app from Finder crashes with:
on any machine where node is installed only via nvm (no Homebrew/system node). Root cause, traced and reproduced on a real machine:
PATH=/usr/bin:/bin:/usr/sbin:/sbin, so every standard candidate inresolveNodeBin()misses.$SHELL -lic 'command -v node'and took the last line of stdout — but rc files freely print to stdout. iTerm2 shell integration emits OSC 1337 escape sequences without a trailing newline, so the captured "line" was\x1b]1337;RemoteHost=...\x07/Users/u/.nvm/versions/node/v20.20.0/bin/node—existsSync()rejects it.'node'→ ENOENT, andstartServer()had no'error'handler on the child, so it surfaced as an Uncaught Exception dialog.Fix
desktop/node-resolve.js(unit-testable;main.jsrequires electron and can't load undernode --test).src/shell-path.jsfor the login-shell step instead of a private probe: its \x01-sentinel capture is immune to rc noise, it logs failures, and its 24h disk cache removes the ~1s interactive shell spawn from cold start. Dev:../src; packaged:extraResourcesapp/src(mirrorsresolveServerEntry).~/.nvm/versions/node/*/bin/nodebefore spawning any shell — fixes nvm-only machines even if the probe fails. The user's~/.nvm/alias/default(exact or prefix form, e.g.20) is honored over the highest installed version, so the scan never silently overrides the default the prebuilt node-pty ABI was working against.'error'instartServer: a clear dialog (install Node 18+ or setCODBASH_NODE) instead of an uncaught exception; SMOKE mode fails loudly with exit 1; the laterwaitForServercatch no longer stacks a second, misleading dialog on top.desktop/package.json: addnode-resolve.jstobuild.files.Verification
test/desktop-node-resolve.test.js(TDD, red→green); full suite 270 pass / 2 pre-existing skips. InjectedexistsSyncfakes are scoped to the fake home so the suite is independent of the host's/usr/local/bin/node.CODBASH_SMOKE=1) under a stripped Finder PATH (env -i ... PATH=/usr/bin:/bin:/usr/sbin:/sbin) →[desktop] SMOKE OK, resolver returns the nvm-defaultv20.20.0(previously: crash).🤖 Generated with Claude Code
https://claude.ai/code/session_01F3q8EdVFkKPTtSGhAU1Wxf