Repository navigation
[Bug][Windows] Bare npm reinstall can leave a live proxy on a mixed old/new module graph #2459
Description
Client or integration
Codex App
Area
Service lifecycle
Summary
On Windows, a bare global npm reinstall performed while an existing OpenCodex Bun proxy is still serving can leave one process with an old in-memory module graph while later dynamic imports resolve against the newly replaced package tree. The proxy remains bound to port 10100 and /healthz can still answer, but every POST /v1/responses fails with a named-export error until the old process is fully terminated and the service is restarted.
ocx update is not implicated: v2.31.0 already stops a live proxy before replacement specifically to prevent mixed old/new code. This report is limited to a bare npm install -g reinstall/fallback performed while a service-managed proxy remains live, followed by ocx service repair accepting that stale process as healthy.
This is different from #1849: the replacement package tree was complete and valid. The failure was a live-process / new-package split, not a missing-file skeleton. It is also distinct from #764's stop/wrapper resurrection and the auto-update health behavior discussed in #281.
Reproduction
Observed sequence; not yet reduced to a clean two-version standalone reproducer. The pre-update package version was not captured.
- Start the Windows Task Scheduler service/proxy on port 10100 with an older global npm installation.
- While that Bun process is still alive, run the immutable replacement command
npm install -g @bitkyc08/opencodex@2.31.0. - The existing Bun PID remains alive after the package files are replaced.
- Send a normal Codex App request. In the observed session this reached the Windows collaboration/process-helper import path; no standalone HTTP-only reproducer has yet been run.
- The request fails repeatedly with a named-export error, although the named export exists in the new on-disk file.
- Fully stop the old Bun/VBS/CMD process chain and start the service again. Requests recover immediately.
Evidence from the observed run:
- Live Bun process creation: approximately 10 minutes before the npm package/source mtimes.
- Live process parent no longer existed; the runtime-port journal referenced a different stale PID.
- Installed package after replacement:
2.31.0. - The current on-disk
src/lib/windows-elevation.tscontainedresolveTrustedWindowsTaskkillExe, while the live process reported that export as missing. - The error repeated for every
/v1/responsesrequest until process replacement.
Expected behavior:
- A bare npm reinstall followed by
ocx service repairshould not allow a live proxy to serve from a mixed module graph; or - repair should detect a proxy whose start time/package identity predates the installed package and replace it before reporting health; or
- the npm fallback documentation/output should require a verified service stop before replacement.
Actual behavior:
- Port 10100 remained occupied by the pre-update Bun process.
- Health/port presence did not prove that request handling was valid.
- Requests failed until the entire old process chain was replaced.
Version
Pre-update version: not captured. Final installed version at failure time: @bitkyc08/opencodex@2.31.0.
Operating system
Windows 11 Home, build 10.0.26200.
Provider and model
Not provider-specific. The failure occurred before provider translation completed.
Logs or error output
SyntaxError: Export named 'resolveTrustedWindowsTaskkillExe' not found in module 'C:\Users\<USER>\AppData\Roaming\npm\node_modules\@bitkyc08\opencodex\src\lib\windows-elevation.ts'.
POST - http://127.0.0.1:10100/v1/responses failedThe same pair repeated for each attempted response. The new on-disk file did contain the export, which is why this points to an old in-memory module graph rather than a missing export in 2.31.0.
Screenshots and supporting files
No private screenshots attached. Process timestamps, task state, package mtimes, listener ownership, and redacted service-log lines were captured during diagnosis.
Related issues: #1849 (incomplete package replacement), #764 (Windows service stop/wrapper resurrection), and #281 (update/health behavior). This report is specifically the complete-new-package + still-live-old-process split on the bare npm reinstall path.
Redacted configuration
{
"service_backend": "scheduler",
"listen_port": 10100,
"install_method": "npm -g",
"installed_version_after_replacement": "2.31.0",
"secrets_removed": true
}Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
lidge-jun commentedon Aug 24, 2026
리뷰 · 우선순위 63 / 80
설명: 이 이슈는 윈도우에서 전역 엔피엠으로 패키지를 다시 깔 때, 이미 떠 있는 번 프록시를 안 멈추면 한 프로세스가 예전 모듈 지도를 들고 새 파일을 읽게 된다고 한다. 포트 10100 은 그대로 잡혀 있고 헬스 주소는 대답한다. 그런데 응답 길
POST /v1/responses는 이름 보낸 오류로 죽는다. 예전 프로세스를 완전히 죽이고 서비스를 다시 켜야 요청이 살아난다. 작성자는 업데이트 명령이 아니라 맨손 엔피엠 다시 깔기라고 못 박았다. 업데이트 2.31.0 은 이미 산 프록시를 먼저 멈춘다. 이 보고는 서비스가 관리하는 프록시가 살아 있는 채로 패키지만 바뀐 길이다. 그 다음 서비스 수리가 그 낡은 프로세스를 건강하다고 받는다. 지금 CURRENTdevHEAD 는35a89903c이다. 이번 시간에 origin/dev 는 그대로다. 새 머지는 없다. package.json 은 2.27.0 이다. src/config.ts 는 3238줄이다. src/runtime 폴더는 지금 HEAD 에 없다.살아 있는 재현은 이렇다. 윈도우 작업 스케줄러 서비스가 예전 전역 설치로 포트 10100 에서 프록시를 켠다. 그 번 프로세스가 살아있는 동안 새 패키지 2.31.0 을 전역으로 덮어 쓴다. 기존 번 피아이는 파일이 바뀐 뒤에도 살아 있다. 코덱스 앱 요청이 윈도우 권한 올리기 가져오기 길로 간다. 디스크의 새 파일에는
resolveTrustedWindowsTaskkillExe가 있다. 그런데 산 프로세스는 그 보낸을 없다고 한다. 같은 쌍이 응답 길마다 반복된다. 부모 프로세스도 이미 없고, 런타임 포트 기록의 피아이디도 다른 낡은 값이다. 프로세스 시작 시각이 패키지 파일 시각보다 대략 십 분 빠르다. 기대는 맨손 다시 깔기 뒤에 서비스 수리가 낡은 모듈 지도를 건강하다고 보지 않는 것이다. 또는 수리가 시작 시각과 패키지 신원을 비교해서, 설치보다 오래된 프록시를 건강 보고 전에 바꾸는 것이다.지금 HEAD 를 열었다. src/lib/windows-elevation.ts 215줄에
resolveTrustedWindowsTaskkillExe가 있다. 새 패키지 파일에는 그 이름이 맞다. 작성자가 본 오류는 파일이 비어서가 아니다. 산 프로세스가 예전 지도를 들고 새 파일을 읽어서다. 1849 는 교체가 덜 된 뼈대다. 이번은 새 패키지가 완전한 뒤에도 예전 프로세스가 남는 갈라짐이다. 764 는 윈도우 서비스 중지와 래퍼가 다시 살아나는 길이다. 281 은 업데이트와 헬스다. 셋 다 닫지 말 것. 2412 는 패키지 관리자가 심과 백업을 같이 지우는 다른 구멍이다. 이번은 심이 아니라 살아 있는 프록시 모듈 지도다. 2412 와 한 기차에 섞지 말 것.서비스 쪽을 보면 구멍은 헬스 기준이다. src/server/proxy-liveness.ts 222줄은 헬스 주소가 프로세스만 대답하면 살아 있다고 본다. 레디니스는 코덱스 동기만 더 본다. 둘 다 패키지 신원과 프로세스 시작 시각을 비교하지 않는다. src/service.ts 3239줄 상태 보고는 프록시가 포트에서 대답하는지만 본다. 3147줄 낡은 표시는 구운 경로와 자산이 없을 때다. 패키지가 바뀌었는데 프로세스가 예전인 경우는 낡은 것이 아니다. 그래서 수리가 그 프로세스를 건강하다고 받는다. 업데이트 일은 이미 산 프록시를 멈춘다. 그 길을 넓히지 말 것. 고침은 맨손 다시 깔기 길과 수리의 건강 기준만 좁게 둔다. 수리가 프로세스 시작이 패키지보다 오래면 교체하게 한다. 헬스만으로 응답 길이 살아 있다고 말하지 말 것.
사용자 길이로는 다시 깐 뒤에 코덱스 앱 요청이 전부 이름 보낸 오류로 죽어서 63. 헬스가 초록이라 수리가 조용히 넘어간다. 카탈로그 팁은 Ox Alpha x-preview-f-free + deepseek-v4-flash-vision-exp. Cursor 정적 카탈로그는 opus-4-8-fast / opus-5-fast. 2334 CursorCredentialRouter 는 여전히 src/providers/cursor-pool.ts 모듈+테스트만 있고 어댑터에 연결되지 않았다. 2332 H2 는 discovery 전용. 2320 overflow + 2342 는 이미 dev. 2188 사이드카는 이미 dev. 2361
__omit__는 이미 HEAD 다. combo-stream-preflight.ts 는 src/server/responses 에 있다. src/lib/bounded-body.ts 128줄은 이미 죽은 시그널에서 취소 없이 던진다. 2426 코덱스 웹소켓 나가는 크기 가드는 없다. 2451 wait 허용 목록은 아직 하이픈이다. 이번 이슈는 그 구멍들이 아니다.src/lib/windows-elevation.ts 라인 215 - 새 파일에는 작성자가 못 찾았다는 보낸이 있다. 산 프로세스 지도가 예전이다
src/server/proxy-liveness.ts 라인 222 - 헬스는 프로세스가 대답만 하면 살아 있다고 본다. 패키지 신원은 안 본다
src/service.ts 라인 3147 - 낡은 표시는 구운 경로와 자산이다. 패키지보다 오래된 프로세스는 여기 없다
src/service.ts 라인 3239 - 상태 보고는 포트 대답만 본다. 응답 길이 이름 보낸으로 죽어도 초록일 수 있다
이슈 2412 - 심과 백업이 같이 사라지는 다른 구멍이다. 이번 모듈 지도와 섞지 말 것
이슈 1849 / 764 / 281 - 덜 된 교체, 래퍼 부활, 업데이트 헬스다. 닫지 말 것
메인테이너의 판단이 필요한 지점
너의 추천
이슈는 열어 둔다. 수리가 패키지보다 오래된 프록시를 건강하다고 보지 않게 한다. 헬스만으로 응답 길을 통과라고 하지 말 것. 업데이트 멈춤 길은 그대로 둔다. 2412 와 1849 와 764 와 281 은 닫지 않는다. 호출 길을 넓히지 말 것. 라벨은 그대로 둔다. 프리뷰 배포가 아니다.
이 댓글은 grok-bot이 작성했습니다
fix(server): refuse requests after the package tree is replaced under…
lidge-jun commentedon Aug 25, 2026
Fixed on
devby #2632 (squash303cc3c8c).Your framing is what made this actionable, particularly the part that is easy to skip past:
/healthzkept answering 200 while everyPOST /v1/responsesfailed. That is the worst possible shape — every supervisor sees a healthy process while the proxy is unusable. The named-export failure is the symptom; the lying health check is what makes it hard to notice.package.jsonfilesystem identity (device, inode, ctime, size) is captured at boot and compared per request. On replacement or unreadability,/healthzand/readyzreturn 503restart_required, and/v1/*returns a typedpackage_tree_changederror naming the cause and the remedy. Management routes stay reachable so you can restart and diagnose through the proxy that just refused you.Identity rather than version, deliberately: reinstalling the same version replaces the file while the version string does not move, and that case has its own test.
Your point that
ocx updateis not implicated held up — it already stops the proxy before replacement. This is specifically about a replacement OpenCodex did not perform, which is why the answer is honest detection rather than coordination.One review addition worth naming: the check is throttled to one filesystem stat per second on the
okpath, because it runs on every/v1/*request and would otherwise add a syscall to the hot path forever to catch an event that happens once per install. A failing reading is never cached, so a repaired install recovers on its own.Not verified on Windows — no Windows host here. The guard is platform-independent and is proven by filesystem and HTTP fault injection rather than by reproducing the npm reinstall.
devlog: record the community bug sweep (#2636)
twk0672005 commentedon Aug 26, 2026
Thanks for the detailed fix and for preserving the distinction from
ocx update. The package-identity approach looks solid.I noticed two post-merge review findings are still unresolved:
/v1/responsesWebSocket does not appear to recheck package integrity for eachresponse.createturn: fix(server): refuse requests after the package tree is replaced under a live proxy (#2459) #2632 (comment)/healthzresponse may omit the restart capability/attestation needed by the recommendedocx restartrecovery path: fix(server): refuse requests after the package tree is replaced under a live proxy (#2459) #2632 (comment)Would you prefer tracking these as follow-up issues, or addressing them before the next release?
I can also verify the final fix on the original Windows environment once a containing release or testable build is available.
fix(server): refuse requests after the package tree is replaced under…
devlog: record the community bug sweep (lidge-jun#2636)
fix(server): refuse requests after the package tree is replaced under…
devlog: record the community bug sweep (lidge-jun#2636)