Repository navigation
ocx sync and restore back fail permanently with an unnamed config issue, leaving the Codex catalog truncated and routing un-injected #1453
Description
Client or integration
Codex CLI
Area
CLI
Summary
ocx sync and ocx restore back fail permanently on my install with a message that names no reason:
Codex sync did not complete. Fix the reported Codex config issue and retry.
Plain `codex` was not switched back to opencodex. Fix the reported Codex config issue and retry.
No "Codex config issue" is ever printed — not on stdout, not on stderr, and ocx doctor reports every check ok. There is nothing to act on, so the instruction "fix the reported issue and retry" cannot be followed.
This has two compounding consequences, because of the ordering documented in src/cli/index.ts:
// Only warn/restart when a catalog or models_cache write actually happened. This is
// deliberately not an `else`: refreshCodexModelCatalog runs before injectCodexConfig,
// so a sync can fail (`ok: false`) after the catalog was already rewritten- The model catalog is rewritten before the injection fails, so it is left truncated. My proxy serves 435 models (200 native, 157 openrouter/opencode-go, 43 Anthropic, 35 Google) but
opencodex-catalog.jsoncontains only 9, all OpenAI-family. The non-OpenAI models are reachable through the proxy but never appear in Codex. - Routing is never restored.
ocx stopandocx updateboth remove the injectedopenai_base_urlandmodel_catalog_jsonroot keys. Sincesync/restore backcan never complete, nothing puts them back. After every update I have to re-add them by hand from<CODEX_HOME>/opencodex.config.toml.
Expected: either the sync succeeds, or the specific config problem is printed so it can be fixed. Ideally the catalog would also not be rewritten until the injection is known to succeed.
Reproduction
100% reproducible on my install, across ocx update, full restarts of the proxy, and two different versions (2.11.0 and 2.12.0).
- Have opencodex injected into Codex (
openai_base_url+model_catalog_jsonpresent in<CODEX_HOME>/config.toml). - Run
ocx stop(orocx update, which stops as its first step). The two injected root keys are removed. - Start the proxy again and confirm it is healthy (
/healthz→ 200,/v1/models→ full list). - Run
ocx restore back→ fails with the message above. - Run
ocx sync→ fails with the message above. - Inspect
opencodex-catalog.json→ rewritten, but containing only the OpenAI-family models.
The proxy is healthy throughout. The only way I have found to restore routing is editing config.toml manually using the keys documented in <CODEX_HOME>/opencodex.config.toml.
Things I ruled out
To save triage time, I checked the obvious candidates and none of them apply:
- Config is valid UTF-8 — strict
TextDecoder('utf-8', {fatal: true})decode succeeds. - No duplicate TOML tables — 185
[projects.*]entries, zero duplicate keys. - Size is modest — 33,524 bytes.
- Proxy is healthy —
/healthz200,/v1/modelsreturns 435 entries, live requests succeed. ocx doctoris entirely clean — every checkok, including "Codex restart safety", "Project Codex configs", and "OAuth reliability".- Not the stripped keys themselves — the failure persists identically after routing is manually restored and verified working end-to-end.
Not a duplicate of the nearby catalog issues
- [Bug]: Service restart rewrites the Codex catalog without handling stale app-servers, so routed models stay missing from the App picker #1046 (stale app-servers) states "The catalog file on disk is already correct at that point". Mine is the inverse: the file on disk is wrong — it contains 9 entries while the proxy serves 435.
ocx sync-cachereports the disk catalog was updated and the count stays at 9, so this is not the stale-app-server case. - [Bug]: Routed models are loaded by Codex app-server but missing from Desktop model picker #241 / OpenCodex models missing in Codex App SSH workspaces despite remote catalog injection #851 are picker/transport-level (models loaded but not shown). Here they are absent from the generated catalog itself.
- [Bug] Stale full-config writes can resurrect deleted providers and custom models #1273 covers stale in-memory full-config writes resurrecting deleted providers — a different mechanism; nothing was deleted here.
- ocx start auto-injects openai_base_url and overwrites provider config, breaking Codex Desktop on networks that cannot reach chatgpt.com #1054 is about
ocx startinjectingopenai_base_urlunwantedly. Here the injection fails and never happens.
Suggested fixes
- Print the actual reason.
syncModelsToCodexreturnsok: falsebut the caller only prints a generic sentence referring to a "reported" issue that is never reported. Surfacing the underlying error would make this self-service. - Do not rewrite the catalog before the injection is known to succeed, or roll the catalog back when injection fails — otherwise a failed sync actively degrades a working install by truncating the model list.
- Consider having
ocx doctorexercise the same code path, since it currently reports a fully healthy system whilesynccannot complete.
Version
2.12.0 (also reproduced on 2.11.0)
Operating system
Windows 11 Pro 10.0.26200
Provider and model
Proxy serves 435 models across openai (native), openrouter, opencode-go and anthropic; the Codex-facing catalog is reduced to 9 OpenAI-family entries.
Logs or error output
$ ocx sync
Codex sync did not complete. Fix the reported Codex config issue and retry.
$ ocx restore back
Plain `codex` was not switched back to opencodex. Fix the reported Codex config issue and retry.
$ ocx sync-cache
WARNING: 4 Codex app-server process(es) still running (PIDs: ...). Disk catalog/cache were
updated, but Codex may keep showing the old model list until those processes restart.
# catalog still contains 9 models afterwards
$ ocx doctor
# every section reports ok, including:
# Codex restart safety ok
# Project Codex configs ok no project-local provider bypass detected
# OAuth reliability [OK] x3
# proxy is healthy and multi-provider the whole time:
GET /healthz -> 200
GET /v1/models -> 435 models
{"openai-native":200,"opencode-go/openrouter-other":157,"anthropic":43,"google":35}
# but the Codex-facing catalog:
<CODEX_HOME>/opencodex-catalog.json -> {"models": [ ...9 entries, all OpenAI-family... ]}Screenshots and supporting files
No response
Redacted configuration
{
"providers": {
"openai": { "adapter": "openai-responses", "codexAccountMode": "direct", "authMode": "forward" },
"openrouter": { "adapter": "openai-chat", "baseUrl": "https://openrouter.ai/api/v1" },
"opencode-go": { "adapter": "openai-chat", "baseUrl": "https://opencode.ai/zen/go/v1" },
"anthropic": { "adapter": "anthropic", "authMode": "oauth" }
},
"defaultProvider": "openrouter",
"activeCodexAccountId": "__main__"
}<CODEX_HOME>/config.toml is 33 KB with 185 [projects.*] trust entries, valid UTF-8, no duplicate keys.
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
flyingsquirrel0419 commentedon Aug 11, 2026
Confirmed against current
dev(0c170525c). The destructive ordering is real:syncModelsToCodex()can commit catalog/cache changes beforeinjectCodexConfig()returns a refusal, and an unsuccessful injection message is currently sent through the normallog()channel while the CLI later prints only the generic failure sentence.I cannot reproduce the reporter's exact host-specific refusal without the original
config.toml, so I will not claim that specific trigger yet. I will fix the confirmed boundary narrowly:ocx syncandocx restore back, and seeded catalog/cache artifacts are not degraded.I will keep provider discovery and successful sync behavior unchanged, and I will link a
dev-targeted draft PR with exact-head verification once the patch is ready.Ingwannu commentedon Aug 11, 2026
Implemented the confirmed failure boundary in draft PR #1461, targeting
dev.Current head:
9e5cc13f53f9cf162b0cf7ce395261462f914273The patch now runs the real injection transformation and coordination-eligibility checks in a no-write preflight before provider discovery or catalog/cache replacement. A deterministic refusal therefore returns before catalog refresh, preserves existing catalog/cache bytes, and prints the concrete refusal on stderr. The normal injector still revalidates at the final write boundary, so the preflight is not treated as a reusable authorization result.
Verification on latest
dev:tests/codex-shim.test.tsservice-token fixture mismatch; a clean latest-devworktree reproduces the same failure independently (34 passed, 1 failed)The issue remains open pending independent review and integration. Review was requested from @lidge-jun and @Wibias.
docs(devlog): plan the seven paired bug-fix landings
Merge pull request #1461 from lidge-jun/agent/fix-1453-sync-transaction
lidge-jun commentedon Aug 11, 2026
Fixed on
devby #1461 (merge849ab5e35).ocx syncandocx restore backnow run the injection transformation and coordination-eligibility checks in a non-writing preflight before provider discovery or any catalog/cache replacement, and the concrete refusal goes to stderr instead of being swallowed by the generic summary. That closes both halves of this report: the message names a reason you can act on, and a deterministic refusal can no longer leave the catalog truncated behind it.Verified on the merged tree:
tests/cli-restore-back.test.tsandtests/codex-sync-api.test.ts16 pass / 0 fail.PRs here target
dev, so GitHub does not auto-close linked issues; closing manually.MarcusNeufeldt commentedon Aug 12, 2026
Root cause found on my host, and it turns out to be residue from #1454 — the two issues were not independent.
What was blocking the sync
A failed
ocx service install(the one reported in #1454) died at the UAC gate after writing its canonical service assets. Those assets were never cleaned up, and they sat in<OPENCODEX_HOME>for six days:Every
ocx syncandocx restore backfrom that moment on refused. Moving just those three files aside made the very nextocx syncsucceed immediately:The Codex-facing catalog went from 9 entries to 439 (202 openai / 159 opencode-go+openrouter / 43 anthropic / 35 google), the routing keys were re-injected automatically, and
<CODEX_HOME>/opencodex.config.toml— which had also gone missing — was regenerated. Nothing else on the host changed.Why 2.13.0 still printed nothing
The fix in #1461 instruments the injection preflight, which is the right fix for that path. But my refusal was taking the earlier branch in
syncModelsToCodex, which returns before the preflight and emits nothing:So on 2.13.0 the CLI still printed only
Codex sync did not complete. Fix the reported Codex config issue and retry.with no reason on stdout or stderr.admitCodexWritehas four authorities (config | generation | service-home | external-provider); as far as I can tell only the preflight path reports.What made this hard to find from outside
Throughout the six days, every diagnostic reported healthy:
ocx service status→service not installed (Task Scheduler)ocx doctor→ every sectionok, including "Codex restart safety" and "Project Codex configs"/healthz→ 200,/v1/models→ 435 models, live requests succeededNothing surfaced that a write veto was in force, or that inert files from an aborted install were the reason.
Suggestions
service-homerefusal too (and the other admission authorities), not just the injection preflight. A one-linelog?.error(admission.message)on that branch would have made this self-diagnosable on day one —CodexAdmissionalready carries amessage.ocx doctor, which currently reports a fully healthy system while every sync is being refused.Environment: reproduced continuously on 2.11.0, 2.12.0 and 2.13.0; Windows 11; single Codex account;
codexAccountMode: direct. Happy to provide anything further from the host if useful.docs(devlog): plan the seven paired bug-fix landings
Merge pull request lidge-jun#1461 from lidge-jun/agent/fix-1453-sync-…