Repository navigation
Windows: bare ocx service on an already-installed service fails with WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED instead of repairing/restarting #2287
Description
Client or integration
Codex CLI
Area
Service lifecycle
Summary
On Windows, running ocx service while the Task Scheduler service is ALREADY installed and running fails with WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED instead of refreshing/restarting the existing service. The bare ocx service path unconditionally goes through install (schtasks /create /f), which requires elevation, so a routine dogfooding restart (update source, then ocx service) dies on UAC even though nothing about the registration needs to change.
Expected behavior: when the service is already installed, ocx service (no subcommand) should take the repair/restart path that ocx service repair provides (no admin re-prompt), or at minimum detect the existing registration and fall back to repair instead of failing with an access-denied error. Additionally there is no ocx service restart subcommand, which is the verb an operator reaches for first; the usage line offers install|repair|start|stop|status|uninstall|remove.
Reproduction
- On Windows, install the background service once (elevated):
ocx service-> approve UAC. Service runs fine (ocx service statusreports Task Scheduler enabled, proxy on port 10100). - Update the source checkout the service points at (dogfooding path:
git pullondev). - From a NON-elevated PowerShell, run
ocx serviceto pick up the new code. - Observe the failure below; the service is not restarted.
ocx service restartis rejected as an unknown subcommand.
ocx service repair is the documented no-admin path, but the bare ocx service invocation (which the CLI describes as "installs/updates and starts the background service") does not route an already-installed service to it.
Version
2.27.0 (local dev checkout, service installed via Task Scheduler)
Operating system
Windows 11
Provider and model
No response
Logs or error output
PS C:\Users\super> ocx service
[X] Service install cleanup failed: Windows access denied while running Task Scheduler. Command: schtasks /create /tn opencodex-proxy /xml C:\Users\super\.opencodex\opencodex-service-task.xml /f Approve the Windows UAC prompt to install the background service, or run `ocx service install` from an elevated PowerShell window.
OCX_ERROR_CODE=WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED
PS C:\Users\super> ocx service restart
Usage: ocx service [install|repair|start|stop|status|uninstall|remove] [--native|--scheduler]
With no subcommand, installs/updates and starts the background service.
repair: refresh assets and restart an already-installed service (no admin re-prompt).Screenshots and supporting files
No response
Redacted configuration
No response
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
Ingwannu commentedon Aug 21, 2026
Confirmed against current
dev@27764f34259023d88ebe1cdc63ecb13e34d2ab64.The report is correct:
normalizeServiceSubcommand(undefined)currently forces the bare command toinstall, and the Windows scheduler install path can reachschtasks /create /feven when the service is already registered. That turns a routine non-elevated refresh/restart intoWINDOWS_SCHTASKS_CREATE_ACCESS_DENIED.I will keep the fix narrow:
ocx servicewill use the existing no-admin repair path when a service is already installed, while preserving first-time install behavior when none exists;ocx service restartwill be an explicit alias for that same repair/restart path rather than a second lifecycle implementation;--native/--schedulerwill remain install-only and will not silently switch an installed backend;I will open the implementation against
devand keep this issue open until the PR has the required independent maintainer approval and exact-head CI.lidge-jun commentedon Aug 21, 2026
리뷰 · 우선순위 58 / 80
지금
devHEADd23c3179f에서도 그대로임.src/service.ts:3276-3278normalizeServiceSubcommand(undefined)가"install"로 고정됨.parseServiceArgs(:3290-3308)가 그걸serviceCommand에 넘김. 헬프는 "no subcommand, installs/updates and starts"라고 쓰는데 (:3461-3463) 이미 깔린 스케줄러는 업데이트 경로가 아님. Windows 스케줄러case "install"(:3344-3366)은scheduler.status === "absent"일 때만installFreshWindowsSchedulerSafely(). 이미 있으면installServiceSafely(backend, ops.install)로 감. 그게prepareServiceInstall(:2834)로 살아있는 매니저/프록시를 먼저 죽임. 그다음installWindows()(:2257-2266)가schtasks(buildWindowsSchtasksCreateArgs())를 탐. 인자는/create /tn opencodex-proxy /xml ... /f(:1632-1634,TASK = "opencodex-proxy":53). 비관리자 셸이면WINDOWS_SCHTASKS_CREATE_ACCESS_DENIED(src/lib/windows-elevation.ts:233-234). 본문 로그랑 같음. ㅋㅋ 소스 땡긴 다음 재시작인데 설치 트랜잭션으로 들어감.더 큼. 실패가 커맨드만 빨갱이인 게 아님.
prepareServiceInstall이/create보다 앞임. 이미 돌던 스케줄러를 멈추고,schtasks /create /f가 UAC에서 죽음. 캐치 (:3367-3370)가 "Service install cleanup failed"를 찍음. 살아 있던 프록시가 그 상태로 남을 수 있음.ocx service repair가 우회인 건 맞는데, 배어 커맨드가 그 우회를 타기 전에 한 번 죽임.이미 있는 길이
repairService임 (:2292-2322). 주석이 그거임. Windows scheduler는 에셋 다시 쓰고 stop/start.schtasks /create없음. UAC 없음.serviceReinstallArgs()(:276-278)도 이미["service", "repair"]를 줌. 업데이트 경로가 repair인 걸 코드가 알고 있음. 배어ocx service만 모름.ocx service restart는default분기로 떨어짐 (:3460). 동사 없음. 별도 라이프사이클을 만들 일 아님. repair 별칭이면 됨. Ingwannu가 이슈에 박은 범위랑 같음.--native/--scheduler는 이미 install-only (:3317-3319). 깔린 백엔드를 배어 커맨드가 조용히 바꾸면 안 됨.테스트가 버그를 잠그고 있음.
tests/service.test.ts:92-95가normalizeServiceSubcommand()==="install"을 기대함. 배어 커맨드 = 설치라고 고정. 이미 설치된 분기에서 repair로 가는 테스트는 없음.fresh Windows scheduler wiring(:1451-1458)은 absent일 때만 fresh 트랜잭션인 것만 봄. 있는 태스크의/create /f는 안 봄. repair 거절 테스트 (:1688-1706)는 미설치/충돌만 봄. 회귀는 (1) 설치됨 → 배어 커맨드 = repair (2) 없음 → install (3)restart= repair 별칭 (4) 백엔드 플래그는 install-only 유지. 이 네 장임.types.ts/config.ts안 만짐. 스플릿 안 씹힘. 리베이스하지 말고 닫으라는 케이스 아님. 닫을 중복 아님. 구현 PR 아직 없음. 이 이슈가 계약 소유자임. #2188 L1–L9 사이드카 이미dev.x_search넣지 말 것. Grok OAuth Chat 기본(#2255)/GUI 옵트인 Responses(#2266)/#2283 웹서치 필드랑 다른 레인임. 프리뷰 배포 아님. v2.29.0 이미 태그됨. 핫 크래시는 아닌데 Windows 도그푸딩이git pull다음ocx service한 방에 프록시를 죽이고 UAC에서 죽음. 그래서 58.해결방안: 배어
ocx service는 이미 설치돼 있으면 기존 no-admin repair. 없으면 지금 install.restart는 그 repair의 별칭. 백엔드 스위칭은 install-only 유지. 배어 리프레시에서prepareServiceInstall부르지 말 것. 그건/create전에 살아 있는 서비스를 죽임. 위 네 장 회귀로 잠가라. 스플릿이service.ts를 옮기면 리베이스하지 말고 닫고 다시 짜라. 지금은 그 정도 아님.이 댓글은 grok-bot이 작성했습니다
Ingwannu commentedon Aug 21, 2026
Implemented the verified service-selection fix in draft PR #2289.
The patch keeps first-time behavior unchanged, but a bare
ocx servicenow detects an existing registration and uses the no-admin repair/restart path instead of attempting Windows Task Scheduler registration again. It also addsocx service restartas an explicit alias ofrepairand updates the maintained CLI docs.Verification on the rebased head:
The PR is intentionally Draft and requests independent review from @lidge-jun. I will not self-approve or merge it.
Ingwannu commentedon Aug 21, 2026
Status update: draft PR #2289 is now green at exact head
2df92a27038dd6c8de264da2369a59462c65c6ca. The final implementation handles the Grok review requirements: existing installs select no-admin repair, proven absence selects install, an unknown Windows registration state fails closed, and invalid flags are rejected before probing. The issue remains open pending independent maintainer approval; I did not self-approve or merge.lidge-jun commentedon Aug 22, 2026
Fixed by PR #2289 with docs-sync hardening (174f03b), landed on dev via merge train PR #2325. Unknown installation state now fails closed with guidance instead of collapsing into absent and triggering elevated re-registration.
Xxingwan11 commentedon Sep 12, 2026
Follow-up: this appears to be caused by the generated
LogonTriggerlacking aUserId, rather than/finherently requiring elevation. Minimal A/B reproduction and source-level evidence are documented in #4425. AddingUserIdto the same-user logon trigger succeeds without elevation.The repair/restart routing fix from #2289 is still the right change — this is a separate cause of the same symptom, on the fresh-install path (
scheduler.status === "absent"), which #2289 does not cover.