Skip to content

[Bug] ENOENT uv_cwd error dump when working directory is deleted and latest_unavailable in GUI #3400

Closed

Description

@agentHits

Client or integration

CLI

Area

CLI

Summary

Running ocx or ocx start from a working directory that has been deleted or unlinked causes two chained failures:

  1. Startup crash dump to stderr: in src/update/notify.ts and src/cli/star-prompt.ts, checking process.stdin.isTTY triggers Bun's lazy initialization of new tty.ReadStream(0). Bun's stream constructor attempts to validate path "" against process.cwd(). Because the working directory was unlinked, libuv's uv_cwd fails with ENOENT: process.cwd failed with error no such file or directory, uv_cwd.
  2. Dashboard update check failure: the proxy daemon remains running in the background with a deleted cwd. When the GUI dashboard queries GET /api/update/check?tag=latest, the server executes spawnSync for npm view. Because the parent process cwd is deleted, spawnSync fails, returning latestVersion: null and reason "latest_unavailable". The dashboard displays: "Could not read the latest version from npm. Try again later."

Reproduction

  1. Open a terminal and create a temporary directory:
    mkdir -p /tmp/stale-workdir && cd /tmp/stale-workdir
  2. Remove that directory from another process or terminal window:
    rmdir /tmp/stale-workdir
  3. Run ocx start in the original terminal.
  4. Observe the ENOENT process.cwd error dump from internal:fs/streams via interactiveGuardOk.
  5. Open the dashboard update modal at http://localhost:10100/ and observe that the latest version check fails with reason latest_unavailable.

Version

2.41.0

Operating system

macOS 15.x / Linux / POSIX

Provider and model

N/A

Logs or error output

53 | function ReadStream(path, options) {
54 |   if (!(this instanceof ReadStream))
55 |     return new ReadStream(path, options);
56 |   options = copyObject(getStreamOptions(options)), options.decodeStrings = !0;
57 |   let { fd, autoClose, fs: customFs, start, end = @Infinity } = options;
58 |     this[kFs] = customFs || fs, this.fd = null, this.path = getValidatedPath(path);
                                                                                 ^
error: ENOENT: process.cwd failed with error no such file or directory, the current working directory was likely removed without changing the working directory, uv_cwd
      at ReadStream (internal:fs/streams:58:77)
      at new ReadStream (node:tty:11:23)
      at interactiveGuardOk (.../src/update/notify.ts:126:40)
      at shouldConsider (.../src/update/notify.ts:139:8)
      at maybeShowUpdatePrompt (.../src/update/notify.ts:233:22)
      at handleStart (.../src/cli/index.ts:310:9)

Redacted configuration

No response

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. added
    bugSomething isn't working
    cliCLI, config inject, packaging flags
    guiDashboard, tray, settings UI
    on Sep 3, 2026
  2. lidge-jun commented on Sep 3, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 54 / 80

    이 이슈는 ocx / ocx start를 이미 지워진(또는 unlink된) 작업 디렉터리에서 실행했을 때 생기는 두 갈래 고장이에요. 지금 dev HEAD(b2954397b, GUI page polish #3397까지)에는 아직 이 방어가 없습니다.

    첫 번째 갈래는 CLI 시작 직후입니다. src/update/notify.ts의 interactiveGuardOk()와 src/cli/star-prompt.ts의 maybeShowStarPrompt()가 process.stdin.isTTY / process.stdout.isTTY를 읽습니다. Bun에서는 이 접근이 게으르게 new tty.ReadStream(0)을 만들고, 스트림 생성자가 빈 path를 process.cwd() 기준으로 검사합니다. cwd가 이미 사라졌으면 libuv uv_cwd가 ENOENT를 내고, 호출자 try/catch보다 먼저 stderr에 긴 덤프가 찍힙니다. 재현 경로(임시 폴더 만들고 다른 창에서 rmdir한 뒤 같은 셸에서 ocx start)도 명확합니다.

    두 번째 갈래는 프록시가 그 깨진 cwd를 그대로 물고 살아남을 때입니다. 대시보드가 GET /api/update/check?tag=latest를 치면 서버 쪽이 npm view를 spawnSync로 돌리는데, 자식이 부모의 삭제된 cwd를 물려받으면 실패하고 latestVersion: null + reason latest_unavailable이 됩니다. GUI에는 “Could not read the latest version from npm…” 문구로 보입니다. 즉 CLI 덤프뿐 아니라 업데이트 배지/모달까지 같이 깨지는 연쇄입니다.

    지금 dev에서 관련 코드는 아직 예전 형태입니다. notify.ts의 interactiveGuardOk는 process.stdin.isTTY를 쓰고, star-prompt.ts도 같고, bin/ocx.mjs / src/cli/index.ts 맨 앞에 cwd 복구가 없습니다. 고쳐 주는 PR은 #3401(fix/heal-deleted-cwd-and-isatty-guard)이고, Closes #3400으로 이미 묶여 있습니다. types.ts/config.ts 분할 캠페인과는 무관해서 close-don't-rebase 대상이 아닙니다.

    우선순위 54로 둔 이유는, 재현이 확실하고 사용자에게 보이는 실패지만, “폴더를 지운 뒤 그 셸에서 계속 실행”이라는 비교적 드문 POSIX 엣지라서 지금 dev의 GUI polish 메인 트레인(#3397/#3396/#3395…)보다는 한 칸 아래이기 때문입니다. 그래도 CLI+GUI가 같이 깨지므로 “나중에 하자” 수준은 아닙니다.

    라인 - (이슈라서 단일 라인 없음) 재현 스택의 interactiveGuardOk / maybeShowUpdatePrompt / handleStart로 이어지므로, 수정 검증은 “덤프 없음”뿐 아니라 대시보드 update check가 latest_unavailable이 아닌지도 같이 봐야 합니다.
    경로/심볼 - src/update/notify.ts interactiveGuardOk · src/cli/star-prompt.ts maybeShowStarPrompt · 업데이트 체크의 spawnSync(src/update/ job/index 쪽)가 이 이슈의 핵심 세 지점입니다.
    경로/심볼 - 버전 표기는 2.41.0이지만 현재 dev package는 2.42.0 라인입니다. 동작 자체는 HEAD에도 동일하게 남아 있을 가능성이 큽니다(위 코드가 아직 예전 형태).

    메인테이너의 판단이 필요한 지점

    • #3401을 이 이슈의 정답으로 바로 묶을지, 아니면 cwd heal을 홈으로 돌리는 부작용(상대 경로 argv)까지 별도 정책으로 볼지
    • “서비스/데몬이 이미 깨진 cwd로 떠 있는” 기존 프로세스까지 고칠지, 아니면 다음 기동부터만 고칠지(이 이슈 범위는 기동 시점)
    • Windows에서 동일 재현이 있는지(보고는 macOS/Linux/POSIX) — Windows cwd 삭제 시나리오를 같은 PR에 넣을지

    너의 추천

    이 댓글은 grok-bot이 작성했습니다

  3. lidge-jun commented on Sep 4, 2026

    @lidge-jun
    Owner

    Fixed by #3401, squash-merged into dev as 0f2e120.

    Both chained failures are addressed. The TTY guard now probes isatty(0)/isatty(1) instead of process.stdin.isTTY, so Bun never lazily constructs tty.ReadStream against the unlinked working directory and the ENOENT uv_cwd dump is gone. Both launch paths recover to homedir() when cwd() fails, so the daemon's later npm view subprocess inherits a valid directory and the dashboard update check stops reporting latest_unavailable.

    One follow-up worth noting: the regression in tests/update-notify.test.ts covers the TTY guard under an unlinked cwd but does not spawn a launcher, so the cwd healing itself is not yet asserted end to end.

    Closing manually: these pull requests target dev, and GitHub only auto-closes linked issues on the default branch.

    Thanks @agentHits.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcliCLI, config inject, packaging flagsguiDashboard, tray, settings UI

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions