Repository navigation
[Bug]: decodeSchtasksOutput UTF-8 fallback mangles GBK schtasks output on zh-CN Windows — service repair/install fails with CJK account name #4691
Description
Summary
On a zh-CN Windows host (OEMCP/ACP 936), ocx service repair — and the Dashboard repair/install buttons — fail against a registration that OpenCodex itself created:
Service repair failed: Task Scheduler registration is not a recognized legacy OpenCodex definition; it was preserved for manual review.
Root cause: redirected schtasks /query /xml output follows the console output code page of the spawning process tree, not the XML document encoding. In any 936 console context (including the no-console background service on a zh-CN host), the bytes are GBK. decodeSchtasksOutput (src/service/windows-scheduler.ts, moved from src/service.ts in the 2.54 refactor) probes UTF-16 and then falls back to a plain UTF-8 decode, which turns the CJK account name inside <SessionStateChangeTrigger><UserId> into U+FFFD replacement characters. The correctly resolved expected identity [SID, MACHINE\user] then never matches the trigger scope, windowsTaskRegistrationHealthy() returns false, and repair aborts at the recognition gate. The same mojibake also rolls back fresh installs at the post-create verification ("...registration was created but failed the OpenCodex action/trigger or attempt-ownership verification. The invalid registration was rolled back.").
This is the remaining unwired call site of the decoder the project already built for exactly this class: decodeWindowsTextBytes (src/lib/windows-text.ts, UTF-16 → strict UTF-8 → locale legacy page incl. GBK) already fixed the sibling whoami/PowerShell decode in src/lib/windows-user-principal.ts (#2914, and #722 for CP949). Related: #3064 (non-ASCII profile path), #4106 (closed as not-planned; that reporter's console was CP 65001 — a different manifestation. The failure below reproduces deterministically under any 936 console).
Environment
- OS: Windows 11, system locale zh-CN (ACP=936, OEMCP=936)
- Local account name contains CJK characters
- opencodex: observed on 2.47.0 → 2.55.0; dev branch fetched 2026-09-08 still has the same fallback
Reproduction
ocx service installon a zh-CN host with a CJK account name. The task registers with SID-scoped session triggers; Task Scheduler export canonicalizes the SID back to the CJK account name.- From a console with
chcp 936(or via the Dashboard, whose repair child runs without a console under the 936 service context), runocx service repair. - Observe the recognition error above (exit code 1; the subsequent serving check still prints the "repaired and serving" line because the proxy was never stopped).
Instrumented A/B against the real repairService (2.54.0, chcp 936):
statusWindowsXml()returns trigger scopes asMACHINE\????(U+FFFD);resolveWindowsTaskDiagnosticUserId()returns the correct[SID, MACHINE\<CJK-name>];windowsTaskRegistrationHealthy()→ false → gate throws.- Same bytes through
decodeWindowsTextBytes→ correct account name; healthy → true;ocx service repairexits 0.
Suggested fix
One-line delegation plus import (running locally as a hotfix since 2.47.0; every npm i -g overwrite brings the bug back):
import { decodeWindowsTextBytes } from "../lib/windows-text";
export function decodeSchtasksOutput(buffer: Buffer): string {
return decodeWindowsTextBytes(buffer);
}Logs
❌ Service repair failed: Task Scheduler registration is not a recognized legacy OpenCodex definition; it was preserved for manual review.
✅ opencodex service repaired and serving on port 10100. (serving check still passes; exit code is 1)
- I searched existing issues and documentation ([Bug][Windows]: ocx 서비스 상태가 로컬화된 schtasks 출력을 UTF-8로 디코딩함 #722, Windows (zh-CN): ocx sync always refuses — localized schtasks output breaks the task-absence probe, and the 2s full-listing fallback times out #2914, Windows: fresh Task Scheduler service install rolls back when profile path contains non-ASCII characters #3062, Windows:
ocx service installrolls back with a non-ASCII profile path #3064, Windows service management breaks on non-ASCII (Chinese) usernames / UTF-8 codepage: 'ownership could not be proven' and 'registered as .\���� ... rolled back' #4106 are related but cover other call sites/manifestations). - I removed secrets, tokens, account details, request credentials, and personal data.
Activity
lidge-jun commentedon Sep 15, 2026
리뷰 · 우선순위 74 / 80
이 이슈는 zh-CN Windows(ACP/OEMCP 936)에서
ocx service repair/ 대시보드 설치·수리가 자기 자신이 등록한 Task Scheduler 작업을인식 불가 레거시로 잘못 판정하는 버그다. 지금devHEAD270291170(패키지 2.56.0, #4683 tip)에도 리포터가 가리킨 호출 자리가 그대로 있다. 원인 경로는 짧다.schtasks /query /xml리다이렉트 출력은 XML 문서 인코딩이 아니라, 띄운 프로세스 트리의 콘솔 코드 페이지를 따른다. 936 콘텍스트(대시보드 수리 자식, 콘솔 없는 백그라운드 서비스 포함)에서는 바이트가 GBK다. 그런데src/service/windows-scheduler.ts의decodeSchtasksOutput(약 20–41행)는 UTF-16 BOM/패턴만 보고, 아니면 plain UTF-8로 떨어진다. CJK 계정명이 들어 있는<SessionStateChangeTrigger><UserId>가 U+FFFD로 깨지면, 올바르게 풀린 기대 identity[SID, MACHINE\\user]와 절대 안 맞는다. 그 결과windowsTaskRegistrationHealthy()가 false가 되고,src/service/repair.ts약 183–193행 recognition gate가 ‘Task Scheduler registration is not a recognized legacy OpenCodex definition…’를 던진다.프로젝트 안에는 이미 이 클래스용 디코더가 있다.
src/lib/windows-text.ts의decodeWindowsTextBytes(약 101–132행)는 UTF-16 → strict UTF-8 → 로케일 legacy(GBK 포함) 순이다. 같은 헬퍼가windows-user-principal.ts(#2914, #722 CP949), tray, system-proxy,service-manager-probe.ts에서는 쓰이는데, 프로덕션 schtasks 디코드 자리만 아직 UTF-8 fallback이다. 리포터 A/B도 현재 구조와 맞다. 깨진 XML이면 healthy=false, 같은 바이트를decodeWindowsTextBytes로 읽으면 healthy=true·repair exit 0.부가 효과가 크다. UserId가 깨진 채면 SID 스코프 업그레이드가 끝나지 않아
identityUpgradeNeeded(repair.ts약 171–173행)가 매 수리마다 참이 될 수 있다. 그게 #4692 elevated ENAMETOOLONG 경로를 매번 타게 만드는 악화 조건이다, 리포터도 그렇게 연결해 두었다. #4106은 닫힌 not-planned이지만 그때 콘솔이 CP 65001이었다. 이번 재현은 936에서 결정적이라 별건으로 보는 게 맞다.라인
src/service/windows-scheduler.ts20–41 (decodeSchtasksOutput) - UTF-16 다음이 plain UTF-8이라 GBK schtasks 출력이 CJK UserId를 U+FFFD로 만든다.라인
src/service/repair.ts157–193 - 깨진 XML 때문에 healthy=false → ‘recognized legacy…’로 보존·중단. 설치 직후 verify/rollback도 같은 디코더를 탄다.경로
src/lib/windows-text.tsdecodeWindowsTextBytes- 이미 UTF-16→strict UTF-8→locale legacy(GBK)를 구현. schtasks 자리만 미연결.경로
tests/windows/windows-scheduler-install-verification.test.tsdecodeSchtasksOutput- UTF-16LE·plain UTF-8만 고정. GBK/zh-CN fixture가 없다.경로
tests/windows/windows-text-decoding.test.ts- zh-CN GBK 케이스는 이미 있다. schtasks 래퍼 쪽에 한 줄 위임 회귀만 추가하면 된다.메인테이너의 판단이 필요한 지점
decodeSchtasksOutput을decodeWindowsTextBytes에 한 줄 위임할지(권장).너의 추천
한 줄 위임 PR을 바로 받는다.
decodeSchtasksOutput이decodeWindowsTextBytes(buffer)만 호출하게 바꾸고, GBK XML fixture(CJK UserId + expected SID)로 healthy=true 회귀를windows-scheduler-install-verification에 추가한다. #4692는 별 PR로 두되, 이 수정이 먼저면 elevated 경로 진입 빈도가 줄어든다. 이슈는bug/platform/service유지. 2.56.0 promote(#4687)와는 묶지 말 것. 다만 2.56.0 직후 핫픽스 후보로 우선순위 높다.이 댓글은 grok-bot이 작성했습니다
Merge pull request #4749 from lidge-jun/codex/win2-schtasks-gbk
lidge-jun commentedon Sep 16, 2026
Landed via #4749 at 52ec0a5
lidge-jun commentedon Sep 16, 2026
Landed via #4749 at c7924b8.
fix(service): decode schtasks output with the Windows text decoder (l…
fix(service): decode schtasks output with the Windows text decoder (l…
Merge pull request lidge-jun#4749 from lidge-jun/codex/win2-schtasks-gbk