Repository navigation
[Bug][Windows][Codex App Beta]: zero-byte coordinator blocks sync and obscures Desktop picker diagnosis #2291
Description
Client or integration
Codex App
Area
Catalog / models
Summary
On Windows with the current Codex App Beta, a zero-byte native write-coordinator database caused every ocx sync / startup sync to regenerate the routed catalog but refuse the config injection:
A missing coordinator row cannot be initialized while native Codex routing residue exists.
The CLI left the user with a partially updated state: opencodex-catalog.json contained routed models, but config.toml had no model_catalog_json or openai_base_url, so the Beta app restarted onto the native OpenAI catalog. ocx doctor reported the app-server catalog as current and did not identify the rowless coordinator or provide a recovery command.
After moving the proven-empty coordinator file aside, ocx sync succeeded and wrote the two root config keys. The fresh cache then contained 12 rows, including five routed rows. The Beta Desktop picker still displayed only OpenAI models, which is a separate renderer allowlist/compatibility condition related to #241 and the explicit nativeAlias workaround from #1244. This report is primarily about the unrecoverable rowless coordinator state and missing diagnosis; the Beta picker behavior is included so the two failure layers are not conflated.
Expected behavior:
- OpenCodex must not leave or reuse a zero-byte coordinator as permanent authority.
ocx doctorshould identify an empty/unversioned/rowless coordinator and print a safe, actionable recovery.- A failed config injection should be surfaced prominently enough that a successful catalog write is not mistaken for a successful sync.
- The Codex App Beta renderer allowlist should be named separately when the on-disk catalog/cache and app-server side are already correct.
Reproduction
- Use Codex App Beta on Windows with a running OpenCodex service and at least one routed provider.
- Reach a state where the effective coordinator path exists as a zero-byte file while
~/.codex/opencodex-catalog.jsoncontains routed entries. - Run
ocx syncor restart the service. - Observe that provider discovery/catalog generation succeeds, then config injection fails with the missing-coordinator/native-residue message.
- Restart the Codex app-server.
- Observe that the app rebuilds
models_cache.jsonfrom the native OpenAI path because the root injection keys were not written. - Move only the proven-zero-byte coordinator aside and rerun
ocx sync. - Observe successful injection and a deliberately stale 12-row
models_cache.jsoncontaining five routed rows. - Restart the Beta app-server. If no explicit
nativeAliascombo is configured, the Desktop picker may still show only OpenAI models due to the separate renderer allowlist behavior tracked by [Bug]: Routed models are loaded by Codex app-server but missing from Desktop model picker #241/fix(codex): preserve routed models in desktop picker #1244.
Recovery performed on the affected device:
- Verified coordinator: size 0,
PRAGMA user_version = 0, no tables, no rows. - Verified residue: routed
opencodex-catalog.json; integration record missing. - Moved the empty file to a same-directory
.rowless-backup-20260821T2045path; did not delete it. - Ran
ocx sync: exit 0, config injection succeeded. - Ran
ocx sync --restart-codex: the stale app-server and code-mode host stopped and respawned. - Verified fresh state:
model_catalog_jsonandopenai_base_urlpresent; cache has 12 rows and five routed entries; proxy/service healthy.
Version
OpenCodex 2.27.0, dev checkout 3e130d239a98
Operating system
Windows 11 Insider build 26200; OpenAI.CodexBeta 26.727.4816.0 x64
Provider and model
Anthropic OAuth plus OpenCode Go routed models; not specific to one model
Logs or error output
+ 5 models appended to Codex catalog
Codex configuration was not written: A missing coordinator row cannot be initialized while native Codex routing residue exists.
# coordinator inspection before recovery
size: 0
PRAGMA user_version: 0
tables: []
rows: []
residue: catalog
integration record: missing
# after moving only the empty coordinator aside
+ 5 models appended to Codex catalog
Pointed Codex's built-in openai provider at the opencodex proxy (openai_base_url).
All models now route through opencodex proxy (like OpenRouter).
# post-recovery cache
fetched_at: 2000-01-01T00:00:00Z
client_version: 0.0.0
models: 12
routed models: 5The repeated Removed unsupported reasoning efforts: max, ultra warning is a separate runtime skew: OpenCodex selected global codex.cmd 0.141.0 for catalog clamping while the Codex App Beta cache identified client 0.146.0. It did not cause the missing routed rows, but it adds misleading noise during recovery.
Screenshots and supporting files
No screenshot attached. Relevant related work:
- Adopt pre-substrate Codex homes into the write coordinator #1049: adoption of pre-substrate routed homes; explicitly treats existing rowless coordinators as refusal.
- [Bug]: Routed models are loaded by Codex app-server but missing from Desktop model picker #241: Desktop renderer can filter routed rows after app-server
model/list. - fix(codex): preserve routed models in desktop picker #1244: explicit
nativeAlias: truecombo compatibility path.
Redacted configuration
{
"defaultProvider": "openai",
"providers": {
"openai": { "authMode": "forward" },
"anthropic": { "authMode": "oauth", "models": ["<redacted>"] },
"opencode-go": { "baseUrl": "<redacted>" }
},
"combos": {}
}Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
lidge-jun commentedon Aug 21, 2026
Additional evidence from the affected Codex/ChatGPT Beta package after local recovery:
OpenAI.CodexBeta 26.727.4816.0on Windows 11 Insider build 26200.codex-cli 0.146.0-alpha.9.2.model/listrequest withincludeHidden: truereturns all 12 catalog rows.hidden: false:anthropic/claude-fable-5anthropic/claude-opus-4-6anthropic/claude-opus-5opencode-go/muse-spark-1.2-contributoropencode-go/ox-alpha-freelist-models-for-hostwithincludeHidden: true. The availability filter admits every row withhidden: false, and the Codex model menu maps the resultingmodelsarray directly into menu items.Therefore the OpenCodex catalog/config/app-server path is healthy after recovery, yet the user still reports an OpenAI-only picker. The remaining mismatch is downstream in the combined Beta renderer/surface state (or the user is being shown the ChatGPT cloud picker rather than the Codex task picker), not in catalog generation or
model/list.No credentials, account identifiers, or local user paths were included in this diagnostic.
Ingwannu commentedon Aug 21, 2026
Confirmed against current
dev@3e130d239a986a1783ca484aec8d8ce7216ac66e.The report identifies a real split-state failure.
codexWriteCoordinationEligibility()currently treats any existing coordinator pathname as authoritative, including a zero-byte file. The transaction opener then treats that zero-byte file like a newly created database, but initialization correctly refuses once routed native residue is present. As a result, the catalog can be refreshed while config injection is refused, and the next Codex App restart falls back to the native catalog.I will keep the fix scoped to the coordinator/injection boundary and diagnosis:
ocx doctorprint the exact coordinator path, the observed state, and a recovery action instead of reporting only the app-server/catalog layer;I will open this as a Draft PR against
dev; it will remain unmerged pending independent maintainer review and exact-head CI.lidge-jun commentedon Aug 21, 2026
리뷰 · 우선순위 67 / 80
지금
devHEAD3e130d239에서 이 재현이 코드랑 맞음.src/codex/inject-coordination.ts:50-60codexWriteCoordinationEligibility가existsSync(coordinatorPath)만 봄. 0바이트여도 파일이 있으면 {kind: "coordinated"}임. 그다음 락이src/codex/transition-state.ts:349-426openCodexCoordinatorTransaction을 탐. 여기선 이미 0바이트를 레이스 오프너로 보고databaseWasEmpty = before.size === 0(:369).initialize(..., databaseWasAbsent || databaseWasEmpty)(:426). 주석이 "Treat that exact state like ENOENT"임 (:364-368). 락 레이어는 빈 파일을 첫 사용으로 봄. eligibility는 존재만으로 권위 있는 코디네이터로 봄. 이 두 문이 충돌함.충돌이
initialize(:283-306)에서 터짐. 빈 파일+absent 취급이면 테이블 만들고 행이 없음.!existing이면assertInitialStateCanBeCreated()(:269-281).classifyNativeRoutedResidue().kind !== "clean"이면 그 메시지 그대로 던짐 (:276-279). 리포터 로그랑 동일함. 카탈로그 재생성은 코디네이터 행 생성 전에 이미 됨. 주입만 거절.opencodex-catalog.json엔 라우티드 모델 있고config.toml엔model_catalog_json/openai_base_url없음. 부분 성공이 버그임. 의도된 #1049 거절을 0바이트 껍데기에 적용한 거임.왜 치우면 됨. 파일을 같은 디렉터리로 옮기면
existsSync가 false. eligibility가 코디네이터 없음으로 가고 (:61-89), residue면 {kind: "legacy-uncoordinated"} (:84-89). 프리-서브스트레이트 주입 경로. 코디네이터 행을 안 만듦.assertInitialStateCanBeCreated를 안 탐. 그래서 두 번째ocx sync가 주입에 성공함. 복구가 매직이 아님. 0바이트를 "있음=권위"로 분류한 게이트를 우회한 거임. 비전공자가PRAGMA user_version할 일이 아님.테스트가 이 조합을 안 잠금.
tests/codex-transition-state-first-use-regression.test.ts:54-67은 깨끗한 tmp 홈에서 0바이트 = first-use만 봄. residue 없음.:69-79비어 있지 않은 unversioned는 거절. 그건 #1049 계약임. 유지해야 함. 깨진 건 "0바이트 + 라우티드 카탈로그 residue + existsSync". eligibility 테스트가 tests 트리에 없음.inject-coordination.ts:58-60주석이 "unversioned and rowless cases it must refuse rather than adopt"인데 0바이트는 rowless가 아님. 스키마도 행도 없는 껍데기임. 비어 있지 않은 unversioned랑 묶지 말 것.ocx doctor가 이 층을 안 봄.src/cli/doctor.ts:991-1007은 히스토리 코디네이터 네임스페이스만 프로브함. missing이면 "not created yet". 쓰기 코디네이터 파일 크기/user_version/테이블/행은 없음.:1059-1066collectCodexAppServerCatalogState는 앱-서버 vs 온디스크 카탈로그임. 주입 키 유무가 아님. 카탈로그 JSON만 맞고 루트 키가 없어도[OK] current가 찍힘. 리포터가 본 그거임. #241/#1244 픽커 얼라우리스트는 주입 성공 다음 레이어임. 이 이슈에nativeAlias워크어라운드 넣지 말 것. 섞으면 진단이 또 죽음.#2188 L1–L9 사이드카 이미
dev.x_search넣지 말 것. Grok OAuth Chat 기본(#2255)/GUI 옵트인 Responses(#2266)/#2283 웹서치 필드랑 다른 레인임.types.ts/config.ts스플릿 안 씹힘. 스키마 PR 아님. 프리뷰 배포 아님. v2.29.0 이미 태그됨. 헤드3e130d239는 카탈로그 리프레시 데브로그뿐임. 핫 크래시는 아닌데 Windows Codex App Beta가 네이티브 OpenAI 카탈로그로 조용히 떨어짐. 닥터가 OK. 그래서 67.해결방안: 입증된 빈 코디네이터(size 0,
user_version0, 테이블 없음)는 eligibility에서 없는 파일로 취급함. residue면legacy-uncoordinated주입. 깨끗하면 first-use 행 생성.existsSync만으로 coordinated 주지 말 것. 비어 있지 않은 unversioned/#1049 거절은 유지.ocx doctor는 쓰기 코디네이터 경로·크기·버전·테이블·행을 찍고, 빈 파일이면 삭제하지 말고 같은 디렉터리로 옮긴 다음ocx sync라고 말함. sync 출력은 카탈로그 성공이랑 주입 실패를 한 줄로 뭉개지 말 것. 픽커 얼라우리스트는 #241/#1244에 남겨. 스플릿이inject-coordination.ts를 옮기면 리베이스하지 말고 닫고 다시 짜라. 지금은 그 정도 아님.이 댓글은 grok-bot이 작성했습니다
Ingwannu commentedon Aug 21, 2026
Draft PR #2295 is now open against
devat exact head6d5f0cf2c.The implementation includes the owner/Grok review boundary rather than treating
size === 0alone as sufficient:user_version = 0with no tables may use the legacy-uncoordinated boundary;Validation on the PR head: 55 focused tests passed, typecheck passed, privacy scan passed, docs built 393 pages, and
git diff --checkpassed. The unrelated service-token fixture failure noted in the PR body also reproduces on cleanorigin/dev@c0cbe494ein this runner.I requested independent review from @lidge-jun and will not self-approve or merge this PR.
Ingwannu commentedon Aug 21, 2026
PR #2295 has been updated at
a813019c98e8f31513e89a6666ee430d490b9840to fix the false CI exit-1 result. The former failing Linux 2/4 shard now completes all 221 selected files in 19 batches with exit 0; focused tests, typecheck, and privacy scan also pass. The functional zero-byte coordinator fix is unchanged. This stays open pending exact-head CI and independent maintainer approval.Ingwannu commentedon Aug 21, 2026
Draft PR #2295 is now fully green at exact head
a813019c967335f43f42d85448bec20c1abf3814, including the rerunenforce-targetgate. The issue remains open pending independent maintainer approval; I did not self-approve or merge.lidge-jun commentedon Aug 21, 2026
Fixed by #2295, landed on dev via merge train PR #2315 (merge commit ffa6d46). Proven zero-byte coordinator remnants are now routed to the absent-file boundary instead of wedging sync.