Repository navigation
Windows: failed ocx service install leaves proxy stopped and Codex routing un-injected because teardown runs before the elevation check #1454
Description
Client or integration
Codex CLI
Area
Service lifecycle
Summary
On Windows, ocx service install stops the proxy and removes opencodex's injected Codex config keys before it discovers it needs elevation. When the schtasks call is then denied, the teardown is not undone — so a failed install leaves a previously working setup broken.
Observed sequence:
ocx service installstops the running proxy and restores native Codex routing (removingopenai_base_urlandmodel_catalog_jsonfrom<CODEX_HOME>/config.toml).- It then runs
schtasks /create ..., which fails because the shell is not elevated:
❌ Service install cleanup failed: Windows access denied while running Task Scheduler.
Command: schtasks /create /tn opencodex-proxy /xml <OPENCODEX_HOME>\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
- The service is not installed and the proxy is left stopped with routing removed.
ocx statusafterwards reportsRestart safety: native Codex routing (no opencodex restart dependency).
Expected: elevation is checked (or the task creation attempted) before any teardown, so that a permission failure is a no-op; or the teardown is rolled back when install fails.
The message text does anticipate the failure ("Approve the Windows UAC prompt… or run from an elevated PowerShell window"), so the elevation requirement is known in advance — it just is not checked early enough.
Reproduction
100% reproducible on my machine.
- Have a working opencodex install with the proxy running and Codex routed through it.
- From a non-elevated shell, run
ocx service install. - Observe the access-denied error above.
- Check
<CODEX_HOME>/config.toml—openai_base_urlandmodel_catalog_jsonare gone. - Check the proxy — stopped.
Recovering is harder than it should be because ocx restore back and ocx sync both refuse afterwards with a message naming no reason (filed separately). I restored routing by hand using the keys in <CODEX_HOME>/opencodex.config.toml.
Suggested fixes
- Probe for elevation (or attempt the
schtaskscreate) before stopping the proxy and un-injecting the config. A permissions failure should leave the system exactly as it was found. - Failing that, make the cleanup path actually restore what the install path removed — the current message says "Service install cleanup failed", so a cleanup step exists but does not cover the config/proxy teardown.
- Consider detecting non-elevated invocation up front and printing the "run from an elevated PowerShell window" guidance instead of starting the install at all.
Version
2.11.0 (the install path is unchanged in 2.12.0 as far as I can tell from the release notes)
Operating system
Windows 11 Pro 10.0.26200
Provider and model
Not provider-specific.
Logs or error output
$ ocx health --json
{"ok":true,"pid":19940,"port":10100}
$ ocx service install
❌ Service install cleanup failed: Windows access denied while running Task Scheduler.
Command: schtasks /create /tn opencodex-proxy /xml <OPENCODEX_HOME>\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
$ ocx health --json
{"ok":false,"pid":null,"port":null}
$ ocx service status
❌ service not installed (Task Scheduler).
$ ocx status
❌ Proxy: not running
Restart safety: native Codex routing (no opencodex restart dependency)
Service: not installed
# and the injected keys are gone from <CODEX_HOME>/config.toml:
# openai_base_url -> removed
# model_catalog_json -> removedScreenshots and supporting files
No response
Redacted configuration
{
"providers": {
"openai": { "adapter": "openai-responses", "codexAccountMode": "direct", "authMode": "forward" }
},
"codexAutoStart": true,
"activeCodexAccountId": "__main__"
}Windows sandbox mode is unelevated in the Codex config; the shell used was a normal (non-admin) terminal.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
Ingwannu commentedon Aug 11, 2026
Confirmed against current
dev(e63de8d46). The ordering is real:installServiceSafely()callsprepareServiceInstall()beforeinstallWindows(), so the existing managers and standalone proxy can be stopped before the firstschtasks /creatediscovers that elevation is unavailable. Proxy shutdown then removes the managed Codex routing as part of its normal cleanup.I will not add a generic “must be elevated” early return, because the Dashboard currently relies on the structured create-access-denied result to request UAC and finalize the scheduler registration. I will change the Windows scheduler install into an explicit two-phase transaction instead:
prepareServiceInstall(). This phase must not stop a manager or proxy, rewrite canonical service assets, run the task, or touch Codex routing.Regression coverage will prove that direct CLI and Dashboard UAC cancellation perform zero manager/proxy-stop calls, preserve the pre-existing routing bytes, and leave no canonical scheduler asset or install-state write. A successful path will assert the order
register (not run) -> cleanup -> asset publish -> run -> verify/state.I am intentionally keeping existing/conflicting-manager recovery fail-closed unless the previous registration can be identified and restored exactly; I will not broaden this into an unsafe best-effort rollback.
Ingwannu commentedon Aug 11, 2026
Implemented the verified two-phase fix in draft PR #1465:
#1465
The fresh Windows scheduler path now registers but does not run the owned task before any manager/proxy teardown. It then verifies the live Task Scheduler XML and only enters cleanup, canonical asset publication, task start, and install-state publication after registration succeeds. UAC cancellation or denial therefore returns before proxy shutdown or managed Codex routing removal. Existing/conflicting registrations remain on the conservative fail-closed path.
Verification on exact
devbase87e3ff9f6:Codex autostart shim > Unix shim exports persisted service API token before running Codex), also reproduced on cleandev; exact-head focused tests and all other gates above were rerun after the rebaseThe PR remains draft and the issue remains open pending independent security review and a real Windows/UAC smoke test. Review has been requested from @lidge-jun and @Wibias.
docs(devlog): plan the seven paired bug-fix landings
lidge-jun commentedon Aug 11, 2026
Fixed on
dev(1fa020a90+6312aef83, via #1465).ocx service installon Windows now registers the scheduler task before any destructive step. A denied UAC prompt leaves the running proxy and the injected Codex routing untouched, so a failed install can no longer break a working setup — verified against the code rather than the CI badge: no manager stop, no canonical asset write, no routing mutation, and no install-state publication happens before registration succeeds.Rollback additionally proves ownership before deleting: an attempt nonce is carried in the task XML and checked immediately before deletion, and where ownership cannot be proven the CLI reports the residual scheduler state rather than claiming the prior runtime was restored — which is what this thread asked for.
One limitation worth stating: the ownership query and the elevated delete are not a single atomic operation, so a task registered in that narrow window can still be removed. Closing that properly needs an attempt-unique task identity and a real Windows host to validate;
windows N/4is dispatch-only (#1059), so no realschtasksbehavior was exercised here.Verified: 137 pass / 0 fail across the service and elevation suites.
docs(devlog): plan the seven paired bug-fix landings