You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug] ocx sync overwrites hand-edited config.json from stale server memory (GLM-5.3 tune lost); 2.21.0 save path fixed it but sync path question remains #1802
A hand-edited config.json (adding glm-5.3 to the zai provider) was silently reverted by an ocx sync while the long-lived proxy server was running. The running server held the pre-edit config in memory; the sync-triggered service-time save serialized the stale copy over the disk file. This is the bug class from #488 (closed). In 2.21.0 the normal save path was fixed to re-read disk first (verified: a later service-time write preserved the hand-edit), but we have not confirmed whether the ocx sync write path was part of that fix or still uses the stale in-memory config.
Environment
opencodex 2.21.0 (npm @bitkyc08/opencodex), Windows, service mode via scheduled task (opencodex-service.cmd wrapper, bun runtime, start --port 10101)
Config at %USERPROFILE%.opencodex\config.json
Timeline (2026-08-15, America/New_York, EDT)
Time
Event
14:02:56
Hand-edit applied to disk: glm-5.3 added to all six zai provider spots (models, modelContextWindows, modelReasoningEfforts, noVisionModels, preserveReasoningContentModels, modelReasoningEffortMap)
14:03:05
ocx sync run against long-lived server (old PID 45428, started 8/13)
14:04:46
config.json rewritten: tune gone; opencode-go/glm-5.3 auto-added to disabledModels
14:21:56 / 14:22:08
Second re-apply, clobbered again by second ocx sync
Service restart; new server (PID 55940) loads the already-reverted file (so the upgrade gets blamed for a loss that happened 46 minutes earlier)
15:06:28 / 15:06:37
Third re-apply + ocx sync - this one survived, consistent with 2.21.0 save path re-reading disk before writing
15:18:44
Another service-time write; tune still intact
Evidence
Backup diffs: pre-tune vs post-clobber differ ONLY by the disabledModels line; the tune appears in none of the backups because each backup was taken from already-clobbered or pre-tune state.
config-mutation.sqlite records the writes (RCA copy retained).
npm debug log confirms the 2.21.0 install completed at 14:51, after the first clobber at 14:04:46.
Ask
Does the ocx sync path (and any other management/GUI write path) in current code re-read config from disk before saving, or can it still serialize a stale in-memory copy over a hand-edit?
If the sync path still saves from memory, can it adopt the same disk-rebase approach as the fixed save path?
Impact / prevention on our side
Documented runbook guard: hand-edit -> restart proxy before any ocx sync/GUI toggle; snapshot -> validate -> diff after every upgrade/sync; config.json now git-tracked locally for one-command rollback.
#518 - fix(codex): warn or restart stale app-server after sync [open] #576 - fix(codex): warn about stale Codex app-servers after a catalog write [merged] #889 - fix(providers): Copilot mixed-wire routing, DeepSeek service_tier + reasoning replay, Claude 4.6/4.7 1M windows [open] #892 - fix: bound the six remaining retained-state leaks (#820 wave 2) [merged] #1331 - fix(catalog): keep failover combos when live discovery omits members [merged]
📝 Issue Planner
Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.
Create Plan
🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your .coderabbit.yaml:
issue_enrichment:
auto_enrich:
enabled: false
💬 Have feedback or questions? Drop into our discord!
Fixed on dev by #1848 (merged as 73440d4bb) — though the code path was already correct; what was missing was proof.
POST /api/sync calls loadConfig() at its own route boundary (src/server/management/config-routes.ts:383), and syncModelsToCodex writes Codex artifacts rather than OpenCodex config.json. Combined with the 2.21.0 save-path fix, the reported clobber is no longer reachable.
The regression added here starts a server, hand-edits config.json out of band so disk is strictly newer than the server's snapshot, then calls /api/sync and asserts against the FILE — because the failure being prevented is the route persisting a caller-held snapshot back over it. It was driven red first by making the route save its held config: the hand-edited provider disappears and the test fails.
Closing as completed. The change is on dev and ships with the next release; it is not in a published version yet.
Activity
coderabbitai commentedon Aug 16, 2026
🔗 Related PRs
#518 - fix(codex): warn or restart stale app-server after sync [open]
#576 - fix(codex): warn about stale Codex app-servers after a catalog write [merged]
#889 - fix(providers): Copilot mixed-wire routing, DeepSeek service_tier + reasoning replay, Claude 4.6/4.7 1M windows [open]
#892 - fix: bound the six remaining retained-state leaks (
#820wave 2) [merged]#1331 - fix(catalog): keep failover combos when live discovery omits members [merged]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
github-actions commentedon Aug 16, 2026
Maintainer decision respected
A maintainer has reopened this issue. The automated closure has been deactivated.
lidge-jun commentedon Aug 16, 2026
Fixed on
devby #1848 (merged as73440d4bb) — though the code path was already correct; what was missing was proof.POST /api/synccallsloadConfig()at its own route boundary (src/server/management/config-routes.ts:383), andsyncModelsToCodexwrites Codex artifacts rather than OpenCodexconfig.json. Combined with the 2.21.0 save-path fix, the reported clobber is no longer reachable.The regression added here starts a server, hand-edits
config.jsonout of band so disk is strictly newer than the server's snapshot, then calls/api/syncand asserts against the FILE — because the failure being prevented is the route persisting a caller-held snapshot back over it. It was driven red first by making the route save its held config: the hand-edited provider disappears and the test fails.Closing as completed. The change is on
devand ships with the next release; it is not in a published version yet.test(sync): pin that /api/sync reads disk, not the startup snapshot
test(sync): pin that /api/sync reads disk, not the startup snapshot