Repository navigation
Windows: v2.40.0 still misclassifies a valid scheduler task for non-ASCII account names #3320
Description
Client or integration
Codex CLI
Area
Service lifecycle
Summary
On Windows 11, OpenCodex v2.40.0 can report the proxy service as expired and reboot protection as missing even when the Task Scheduler task is enabled and the proxy is running.
Expected: ocx service / ocx service repair should recognize the registered opencodex-proxy task and report the service as healthy.
Actual: the install/repair flow can fail with schtasks.exe /create ... Access is denied. from a normal PowerShell session. After elevated task registration, the health check can still reject the valid task when the Windows account name contains non-ASCII characters. The local proxy remained reachable (the health endpoint returned HTTP 200), so this appears to be a scheduler identity/trigger validation false negative rather than a proxy failure.
This looks like a residual case related to #3064 and PR #3134: paths may be handled, but the <UserId> comparison and/or schtasks output decoding still does not tolerate non-ASCII account names.
Reproduction
Environment: Windows 11 x64; the Windows account/profile directory contains non-ASCII characters. The actual account name and path are intentionally redacted.
- Install
@bitkyc08/opencodex@2.40.0globally. - From a normal (non-elevated) PowerShell, run
ocx service(orocx service install). - Observe a scheduled-task registration/cleanup error similar to:
schtasks.exe /create /tn opencodex-proxy /xml <temporary-stage-path>\task.xml
followed byAccess is denied. - Register or repair the task from an elevated shell.
- Refresh the OpenCodex startup-safety/service health check and observe that a valid enabled/running task can still be reported as expired or as having missing reboot protection.
- Verify independently that the
opencodex-proxytask is running and that the local health endpoint returns HTTP 200.
No real username, machine name, SID, full path, PID, token, or raw log file is included in this report.
Version
@bitkyc08/opencodex@2.40.0
Operating system
Windows 11 x64 (Windows account/profile path contains non-ASCII characters)
Provider and model
No response
Logs or error output
No raw logs attached. Sanitized error shape:
schtasks.exe /create /tn opencodex-proxy /xml <temporary-stage-path>\task.xml
Access is denied.Screenshots and supporting files
No screenshot or supporting file attached; local account, machine, and path details were intentionally omitted.
Redacted configuration
Not provider-specific. No credentials, tokens, account identifiers, or private configuration are relevant.Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
lidge-jun commentedon Sep 3, 2026
리뷰 · 우선순위 64 / 80
이 이슈는 Windows에서 계정 이름(또는 프로필 경로)에 비ASCII 문자가 있을 때, Task Scheduler에
opencodex-proxy작업이 실제로 켜져 있고 로컬 프록시 health가 HTTP 200인데도 OpenCodex가 서비스를 “만료(expired)”이거나 “재부팅 보호 없음”처럼 잘못 판정한다는 보고입니다. 재현 환경은 Windows 11 x64, 패키지는@bitkyc08/opencodex@2.40.0이고, 일반(비관리자) PowerShell에서ocx service/ocx service install을 돌리면schtasks.exe /create ... Access is denied.가 나고, 관리자 권한으로 작업을 등록한 뒤에도 상태/수리 흐름이 유효한 등록을 거부할 수 있다고 합니다. 보고자가 가리킨 선행 이슈는 #3064(비ASCII 프로필 경로로 install이 롤백되던 문제)와 그 수정 PR #3134입니다.지금
devHEAD(bb27c26be, package 2.41.0)를 기준으로 보면 #3134는 이미v2.40.0에도 들어 있습니다. 그 수정의 핵심은src/service.ts의taskXmlLossyValueEquals/taskXmlDecodedLossyValueEquals입니다.schtasks /query /xml이 콘솔 코드페이지 밖으로 나가지 못하는 경로 글자를?나 U+FFFD로 바꿔 돌려보낼 때, OpenCodex가 방금 자기가 쓴Command/Arguments(런처 경로)만 “손실 허용”으로 다시 맞춰 보게 해서 #3064식 롤백을 막습니다. 그래서 “경로만” 깨지던 케이스는 2.40.0에서도 이미 다뤄진 상태입니다. 이 이슈가 남는다면 경로가 아니라 세션 복구 트리거의<UserId>신원 비교 쪽입니다.같은 파일의
windowsTaskTriggerScopeAcceptable(대략 2117–2136행)은 의도적으로 lossy를 쓰지 않습니다. 새 등록은resolvedWindowsTaskSid()로 SID를 XML에 넣고, 상태 판정은cachedWindowsTaskUserIds()가 돌려주는[sid, name]과taskXmlDecodedValueEquals로 정확히 맞춥니다. 주석과 테스트never code-page-folds an explicit session identity(tests/service.test.ts)가 이유를 밝힙니다. 계정 이름을 lossy로???까지 허용하면 비ASCII 두 계정이 같은 치환 문자열로 붕괴해, 고정 이름 작업(opencodex-proxy)을 다른 계정 것으로 수리/채택할 수 있기 때문입니다. 신원은 경로와 달리 “조금만 느슨하게”가 보안 경계입니다.그럼에도 보고 증상(프록시는 살아 있는데 스케줄러 정의만 stale/만료/재부팅 보호 누락)은 이 정확한 UserId 검사에서 나올 수 있습니다. 레거시/수동 등록이 이름 형태(
MACHINE\비ASCII)를<UserId>에 넣었거나,schtasksexport가 이름을 코드페이지로 깨뜨린 채 SID는 없고 깨진 이름만 남은 경우, expected 쪽의 올바른 유니코드 이름·SID와 한 글자도 안 맞아windowsTaskHasSessionRecoveryTriggers가 실패합니다. 그러면windowsTaskRegistrationHealthy가 false가 되고deriveWindowsServiceDiagnostic이 stale로 올라가 “재부팅 보호/세션 복구 트리거 없음”처럼 보입니다. 반대로 healthz 200은 프록시 프로세스와 무관하게 통과할 수 있어, 보고자의 “스케줄러 신원 오탐” 설명과 맞습니다. 일반 셸의Access is denied는 별 트랙입니다.schtasks /create는 관리자 상승이 필요한 등록 경로이고, #2287 계열의 “이미 설치된 서비스에서 create를 다시 치려다 거절”과 겹칠 수 있습니다. 이 이슈의 본진은 상승 실패가 아니라, 상승 후에도 유효 등록을 건강하지 않다고 읽는 판정입니다.정리하면 현재
dev는 경로 mangling(#3064/#3134)과 “UserId는 접히지 않게”라는 정책을 둘 다 갖고 있고, 그 정책이 남은 오탐의 직접 원인 후보입니다. types.ts/config.ts 분할 캠페인과는 무관한platform/service버그라 닫지 말고, 재현 XML(redact된<UserId>형태: SID인지 이름인지, export가?로 깨졌는지)만 확보한 뒤 신원 쪽을 좁혀 고치면 됩니다.src/service.ts · windowsTaskTriggerScopeAcceptable - UserId는 taskXmlDecodedValueEquals만 쓰고 lossy를 금지한다. 이름형 UserId가 schtasks export에서 ???로 바뀌면 세션 복구 트리거가 전부 실패해 건강 등록이 stale/재부팅 보호 누락으로 보인다.
src/service.ts · taskXmlLossyValueEquals / windowsTaskRegistrationBaseHealthy - #3134가 고친 것은 Command/Arguments 경로뿐이라, 보고자가 적은 “경로 말고 UserId” 잔여 가설과 코드가 일치한다. 2.40.0에 이미 들어가 있어도 이 이슈는 설명된다.
src/service.ts · buildWindowsTaskXml / resolvedWindowsTaskSid - 신규 등록은 SID를 넣도록 바뀌었지만, 예전 이름형 등록·수동 XML·export 변형이 남아 있으면 SID expected와 이름 mangling reported가 교차해 영원히 불일치할 수 있다.
src/lib/windows-user-principal.ts · cachedCurrentWindowsIdentity - PowerShell이 돌려주는 name은 유니코드로 올바를 수 있어, schtasks가 깨뜨린 UserId 문자열과 더 어긋난다. 디코딩 버그라기보다 비교 권위(SID vs 이름 vs 깨진 이름) 문제다.
일반 PowerShell의 schtasks /create Access is denied - 상승이 필요한 등록 실패로, health 오탐과 원인을 섞지 말아야 한다. 재현 메모에서는 “비상승 create 실패”와 “상승 후 상태 오판”을 단계별로 나눠 적는 편이 좋다.
메인테이너의 판단이 필요한 지점
너의 추천
열어둠 유지. 보고자에게
schtasks /query /tn opencodex-proxy /xml에서 세션 트리거<UserId>만 redact해 형태(SID / DOMAIN\name / ???)를 알려 달라고 한 뒤, SID가 이미 있는데도 실패하면 identity 캐시/조회 경로를, 이름형·mangling이면 repair의 identityUpgradeNeeded가 실제로 SID 재등록까지 가는지와 상태 문구를 고치는 작은 platform PR로 이어라. #3064/#3134와 중복 닫지 말 것(경로 수정과 잔여 UserId 오탐은 코드상 분리됨). types/config 분할과 무관.이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Sep 3, 2026
@chowyuan1314 — following up during a backlog review. This is not closed; it needs one piece of information from you to move.
Where this stands. Scheduler identity requires exact decoded
UserIdequality atsrc/service.tsand deliberately refuses lossy matching, so distinguishing a SID from a display-name mismatch needs the actual value.What would unblock it. Please add the redacted
<UserId>element fromschtasks /query /xmlfor the affected task — shape only, with the identifier itself masked.If that arrives, this gets picked up in the next triage pass. Without it, the report cannot be distinguished from configurations that already work, so it will keep sitting here rather than being wrong on purpose.
chowyuan1314 commentedon Sep 4, 2026
Thanks. I queried the currently registered
opencodex-proxytask withschtasks /query /tn opencodex-proxy /xml. The redacted element is:This is the SID form, not an account/display-name form.
Important context: this query was performed after applying a local compatibility patch to v2.40.0 and running
ocx service repair, which may have recreated the task. The stock v2.40.0 install/validation failed before that repair. Therefore this confirms the identity shape of the current task, but should not be read as proof that the unpatched build passes. No raw SID, username, machine name, or full path is included.lidge-jun commentedon Sep 4, 2026
@chowyuan1314 — thanks, the SID form is the useful part.
On current
devthe validator should accept exactly what you pasted.cachedWindowsTaskUserIds()insrc/service.tsreturns both spellings —[identity.sid, identity.name]— andwindowsTaskTriggerScopeAcceptableaccepts the trigger if the<UserId>matches either of them. So a SID-form<UserId>is not itself the rejection cause, and neither is a non-ASCII display name, provided the identity resolves at all.That narrows it to one of two things, and your note that the query ran after a patched
ocx service repairis exactly why I cannot tell which from here:cachedCurrentWindowsIdentity()returns nothing,resolveWindowsTaskDiagnosticUserIdfalls through toresolveCurrentWindowsPrincipal, and if that also fails it returnsnull— at which point a scoped trigger cannot be validated and the task is reported unhealthy no matter how correct it is. A non-ASCII account name would plausibly break the lookup's output decoding rather than the comparison.What would settle it, from an unpatched v2.42.x (or current
dev), before anyrepair:ocx service status --json— I am specifically after whether the scheduler detail says the registration is unhealthy versus the XML could not be read, since those are different branches.<Triggers>block ofschtasks /query /tn opencodex-proxy /xml, redacted the same way you did — I need to see whether<UserId>is present and whether the element is namespace-prefixed (<t:UserId>), because a prefixed tag is rejected outright bytaskXmlHasPrefixedTag.ocx service statusbehaves differently in an elevated versus non-elevated shell.If the identity lookup is what fails on a non-ASCII account, that is a real bug and I would rather fix the lookup than the comparison. Keeping this open and labeled
needs-infoin the meantime.docs(devlog): plan cross-platform parity and the Windows identity dec…
fix(windows): decode the principal lookup with the console code page
docs(devlog): plan cross-platform parity and the Windows identity dec…
fix(windows): decode the principal lookup with the console code page
fix(windows): decode the principal lookup with the console code page
fix(windows): decode the principal lookup with the console code page
fix(windows): decode the principal lookup with the console code page (#…
lidge-jun commentedon Sep 8, 2026
Closing as stale needs-info. This is not a judgment that the report was invalid.
The evidence needed to move it is a pre-repair capture from an unpatched build: the
ocx service status --jsonscheduler detail, and the<Triggers>block fromschtasks /query /tn opencodex-proxy /xml, redacted the way you already did. The SID you sharedwas queried after a local compatibility patch and an
ocx service repair, which may have rewrittenthe task, so it confirms the current shape rather than the failing one.
That distinction matters because a SID-form
<UserId>should already validate on currentdev:cachedWindowsTaskUserIds()returns both the SID and the account name, andwindowsTaskTriggerScopeAcceptableaccepts a trigger matching either. The remaining candidates arethat identity resolution fails outright on a non-ASCII account, or that the stock task differs from
the repaired one — and only an unpatched capture separates them.
Please reopen with that capture and it will be picked up. Thank you for confirming the SID shape and
for redacting it carefully.
docs(devlog): plan cross-platform parity and the Windows identity dec…
fix(windows): decode the principal lookup with the console code page (l…