Repository navigation
[Bug]: Codex App update invalidates the persisted codex-runtime.json pin — the dead hashed path is never re-resolved #4035
Description
Client or integration
Codex App
Area
CLI
Summary
~/.opencodex/codex-runtime.json persists an absolute path to the Codex App's bundled CLI, and that path lives inside a content-hashed directory:
%LOCALAPPDATA%\OpenAI\Codex\bin\<hash>\codex.exe
The Codex App replaces that hashed directory on every update and deletes the previous one. The persisted configured pin then points at a path that no longer exists, and runtime selection falls through to fallback:
Codex runtime: codex
Codex version: unknown
Codex source: fallback
⚠️ No validated Codex runtime found (path does not exist); falling back to `codex`.
A dead configured entry is never discarded or re-resolved. Because the Codex App does not put its hashed bin directory on PATH, the env → configured → shim → PATH → fallback chain introduced in #359 has nothing left to find, and codex does not resolve as a bare command on this host.
I expected a recorded runtime path that no longer exists to be treated as absent — dropped, with resolution continuing down the chain and the file re-persisted — rather than retained while runtime selection degrades to a bare codex fallback.
This is not #3442. That fix covers a pin whose path is real but whose recorded version is old (the entitlement client_version floor). This is a pin whose path does not exist at all.
Reproduction
Observed twice on the same host, the second time live within a single 15-minute window.
Instance 1 — how the pin died
- On 2026-09-02,
codex-runtime.jsonrecorded"command": "...\\OpenAI\\Codex\\bin\\b99306303521e97e\\codex.exe","source": "configured","selectedVersion": "0.151.0-alpha.7.2". - On 2026-09-04, the Codex App updated to 0.153.0. The bundled CLI moved to
...\bin\9ba750cce02d5e5c\, andb99306303521e97ewas removed. - From then on,
ocx statusandocx doctorreportedsource=fallback,version=unknown, and the "No validated Codex runtime found (path does not exist)" warning. This survivedocx service restartand an upgrade from 2.42.0 to 2.48.0.
Instance 2 — the same directory replacement observed live
- At 2026-09-08 ~21:26 KST,
...\OpenAI\Codex\bin\contained9ba750cce02d5e5c(holdingcodex.exe,codex-cli 0.153.0) ande5208f10301fe5e6(holdingrg.exe). - A Codex App update was applied at ~21:38 KST.
...\bin\then contained8e5b6932251c2c1candc60635126245daef.9ba750cce02d5e5cno longer existed — roughly 15 minutes after being the live runtime directory.
Any pin written against a hashed directory is therefore invalidated by the next Codex App update.
A stable path exists and is not used
%USERPROFILE%\.codex\plugins\.plugin-appserver\codex.exe carries no hash and is updated in place. Across the update in step 5 it kept the same path and went from codex-cli 0.153.0 to codex-cli 0.153.4. Searching this repository for plugin-appserver returns no results, so the resolver does not appear to consider this location.
Workaround
Repointing command in codex-runtime.json to that stable path restored source=configured with an attested version, and it survived the step-5 update untouched. ocx doctor reports no warnings afterwards.
Analysis and suggested directions
- Validate
configuredat resolution time. When the recordedcommanddoes not exist, treat the entry as absent: continue downenv → configured → shim → PATH → fallbackand re-persist the result, instead of retaining a dead pin and degrading tofallback. - Add the Codex App's stable location as a discovery candidate.
%USERPROFILE%\.codex\plugins\.plugin-appserver\codex.exe(and the macOS/Linux equivalents) is the only Codex App path observed here that survives an update. - Refresh
selectedVersionon drift. After the workaround, the file still records0.153.0while the resolved binary reports0.153.4.ocx statusshows the probed value, so the recorded field goes stale. Harmless here — well above the 0.144.0 gated floor — but it is the same field that tier 2 ofresolveCodexEntitlementClientVersionreads (fix(codex): raise a stale runtime's client version to the measured gated floor #3442).
Two notes on existing recovery paths:
ocx doctor --fix-codex-runtime, added in fix(codex): stable Codex runtime selection for sync/clamp (#297) #359, is not listed inocx doctor --helpon 2.48.0. I did not run it.- Even if it were available, it could not have recovered this host: with the pin dead and no Codex CLI on
PATH, the chain has nothing to resolve to, which is exactly the observedfallbackstate.
Related: #359 (the resolver and codex-runtime.json), #3442 (stale-but-valid pin version), #356 (shim restore after external npm updates — the analogous problem for npm-installed CLIs).
Version
2.48.0
Operating system
Windows 11 Pro 10.0.26200
Provider and model
No response
Logs or error output
# Before — dead pin (2.48.0, after the 2026-09-04 Codex App update)
$ ocx status
Codex runtime: codex
Codex version: unknown
Codex source: fallback
/!\ No validated Codex runtime found (path does not exist); falling back to `codex`.
Run ocx doctor for diagnosis and recovery.
$ ocx system codex-cli-update check --json
{
"candidateAvailable": false,
"versionEvidence": { "kind": "unavailable" },
"provenance": "unknown",
"managed": false,
"reason": "candidate_unavailable",
"location": null
}
# Intermediate — with the hashed dir temporarily added to PATH, selection
# succeeded, but the dead pin was still preferred over it
$ ocx doctor
Codex runtime selection
ok Selected runtime: ...\OpenAI\Codex\bin\9ba750cce02d5e5c\codex.exe (0.153.0, source=path)
!! Preferred runtime unavailable; fell back to ...\bin\9ba750cce02d5e5c\codex.exe
# Hashed directory replacement, observed live
# 21:26 KST
9ba750cce02d5e5c/ e5208f10301fe5e6/
# 21:38 KST, after the Codex App update
8e5b6932251c2c1c/ c60635126245daef/
$ ls .../OpenAI/Codex/bin/9ba750cce02d5e5c
ls: cannot access '.../OpenAI/Codex/bin/9ba750cce02d5e5c': No such file or directory
# The stable path across that same update
# before: codex-cli 0.153.0 after: codex-cli 0.153.4
# path unchanged: %USERPROFILE%\.codex\plugins\.plugin-appserver\codex.exe
# After — pin repointed to the stable path
$ ocx status
Codex runtime: C:\Users\[USER]\.codex\plugins\.plugin-appserver\codex.exe
Codex version: 0.153.4
Codex source: configured
$ ocx doctor
(no warnings)Screenshots and supporting files
No screenshots attached. Every value above was read from ocx status, ocx doctor, ocx system codex-cli-update check --json, directory listings under %LOCALAPPDATA%\OpenAI\Codex\bin\, and codex.exe --version. Account identifiers, tokens, credentials and quota data are excluded.
Redacted configuration
// ~/.opencodex/codex-runtime.json — the recorded command path no longer exists
{
"version": 1,
"command": "C:\\Users\\[USER]\\AppData\\Local\\OpenAI\\Codex\\bin\\b99306303521e97e\\codex.exe",
"source": "configured",
"selectedVersion": "0.151.0-alpha.7.2",
"updatedAt": "2026-09-02T05:57:19.180Z"
}Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
lidge-jun commentedon Sep 8, 2026
리뷰 · 우선순위 68 / 80
이 이슈는 Codex App을 업데이트하면 OpenCodex가 기억해 둔 Codex CLI 경로가 깨지는 버그를 말합니다. 지금
devHEAD29bb221c3(패키지 2.49.0)에는 #359에서 들어온 런타임 선택기가src/codex/runtime.ts에 살아 있습니다. 우선순위는env(CODEX_CLI_PATH) → configured(~/.opencodex/codex-runtime.json) → shim → PATH → fallback입니다. Codex App 쪽 바이너리는 보통%LOCALAPPDATA%\OpenAI\Codex\bin\<내용해시>\codex.exe처럼 해시 폴더 안에 있고, 앱이 업데이트되면 그 해시 폴더 자체가 사라지고 새 해시 폴더가 생깁니다. 그런데 OpenCodex는 예전에 성공했던 절대 경로를codex-runtime.json의configured로 그대로 남겨 둡니다.재현 서술이 아주 구체적입니다. 9월 2일에 핀이
...\bin\b99306303521e97e\codex.exe(0.151.0-alpha.7.2)였고, 9월 4일 Codex App이 0.153.0으로 올라가며 그 폴더가 지워졌습니다. 그 뒤ocx status/ocx doctor는source=fallback,version=unknown, “No validated Codex runtime found (path does not exist)” 경고를 냈고, 서비스 재시작·OpenCodex 2.42→2.48 업그레이드에도 살아남았습니다. 같은 날 21:26→21:38 KST에 해시 디렉터리가9ba750cce02d5e5c에서8e5b6932251c2c1c로 바뀌는 것까지 실시간으로 찍혀 있어서, “한 번 핀한 해시 경로는 다음 앱 업데이트에 무조건 죽는다”가 사실로 확인됩니다. 이건 #3442(경로는 살아 있는데 기록된 버전만 낡은 경우, entitlementclient_version바닥)과 다릅니다. 여기는 경로 파일이 아예 없는 경우입니다.현재 코드 동작을 맞춰 보면,
probeVersion(src/codex/runtime.ts약 286–293행)은 절대 경로가 없을 때path does not exist로 거절합니다.resolveCodexRuntimeUncached(약 546–630행)는 거절된 configured 다음에 shim/PATH/fallback을 이어서 고를 수 있고,replacedConfigured진단도 남깁니다. 문제는 그다음입니다.resolveAndPersistCodexRuntime(약 633–657행)는result.runtime.source !== "fallback"일 때만 디스크에 다시 씁니다. 그래서 해시 핀이 죽고 PATH에도 Codex가 없으면 선택은 fallback으로 떨어지고, 죽은 configured 핀은 파일에서 지워지지 않습니다. 리포터가 본 “경고만 반복되고 핀은 영원히 남는” 증상과 정확히 맞습니다. 게다가pathCandidates(약 363–375행)는 PATH만 보고,shimCandidates는codex-shim.json만 봅니다. 리포터가 찾아 낸 안정 경로%USERPROFILE%\.codex\plugins\.plugin-appserver\codex.exe(업데이트 중에도 경로 불변, 0.153.0→0.153.4)는 저장소에plugin-appserver문자열이 없고 후보 목록에도 없습니다. 임시로 해시 bin을 PATH에 넣으면 doctor가 path로 고르긴 하지만, 평소 Codex App은 그 bin을 PATH에 안 올리므로 일반 사용자는 자동 복구가 안 됩니다.ocx doctor --fix-codex-runtime(src/cli/doctor.ts약 1004–1025행)도 이 케이스의 은탄환이 아닙니다. 새 후보(newerAvailable)가 있을 때만 핀을 갈아끼우고, 없으면resolveAndPersistCodexRuntime만 호출합니다. fallback이면 위와 같이 죽은 핀을 지우지 않습니다. 리포터 말대로 2.48.0ocx doctor --help에 이 플래그가 안 보여도, 플래그가 보여도 “핀 죽음 + PATH 공백”에서는 복구가 안 됩니다. #356(외부 npm 업데이트 뒤 shim 복구)과 같은 계열의 “외부 설치물이 자리를 옮기면 OpenCodex 기록이 따라가지 못함” 문제입니다. 제품 tip(#4002 등)과는 겹치지 않는 Windows Codex App 런타임 생존성 버그라서, types/config 분할 무효화 대상도 아닙니다.라인 293 -
probeVersion이 없는 절대 경로를 거절하는 것은 맞지만, 그 결과가 디스크의 죽은 configured 핀을 지우거나 재탐색을 강제하지는 않는다라인 647 -
resolveAndPersistCodexRuntime가source === "fallback"이면 persist를 건너뛰어, 죽은 핀+공백 PATH 상황에서codex-runtime.json이 영원히 남는다경로/심볼 pathCandidates - PATH만 스캔하고 Codex App의 해시 bin·
plugin-appserver안정 경로를 후보에 넣지 않는다경로/심볼 doctor --fix-codex-runtime -
newerAvailable이 없으면 죽은 핀을 정리하지 못해, 리포터 재현 환경에서는 복구 수단이 되지 않는다경로/심볼 selectedVersion - 우회 후에도 파일의 버전이 실제 바이너리와 어긋날 수 있어(#3442 tier2가 읽는 같은 필드) 드리프트 갱신도 같이 볼 가치가 있다
메인테이너의 판단이 필요한 지점
plugin-appserver및 macOS/Linux 대응)를 정식 discovery 후보로 넣을지--fix-codex-runtime를 help에 노출하고, “핀 무효 시 강제 재탐색/클리어” 의미로 고칠지너의 추천
bug/cli/service로 받아 두고, 1순위는src/codex/runtime.ts에서 존재하지 않는 configured를 absent처럼 취급한 뒤 체인을 계속하며 성공 시 재persist(실패·fallback이어도 죽은 핀은 디스크에서 제거)하는 수정입니다. 가능하면 Windows·macOS·Linux의 Codex App 안정 경로를 discovery 후보에 추가하고, doctor 플래그 help/복구 의미를 맞추세요. 리포터 우회(핀을plugin-appserver로 수동 변경)는 임시책으로 문서에만 남기면 됩니다. 중복 이슈로 닫을 대상은 아니고 #3442/#359/#356을 Related로 유지하세요.이 댓글은 grok-bot이 작성했습니다
fix(codex): retire a runtime pin only on an exact path match
wp4: fix four reported bugs with no owning pull request (#4058)
lidge-jun commentedon Sep 8, 2026
Fixed on
devin ff524ae (via #4058).You were right that the dead pin is never re-resolved:
resolveAndPersistCodexRuntimedegrades tofallbackwhen the hashed path is gone, but the persist branch declines a fallback result, so the staleconfiguredentry survived and every later resolve re-probed a path that cannot exist. The pin is now retired when resolution degrades tofallbackfor that specific reason, and the failure names the persisted command so the log says what was dropped.The retirement is bounded three ways — only a
fallbackresult, only the persisted command matched exactly, and only the path-does-not-exist rejection — so a present-but-unusable binary is still left for you to look at. Review hardened two edges before merge: the comparison is exact rather than case-insensitive, so a missing/plugins/codexcannot retire a live/plugins/Codexon Linux, and a failed removal now warns instead of silently leaving the pin authoritative.Closing manually because pull requests here target
dev, and GitHub only auto-closes on merge intomain.wp4: fix four reported bugs with no owning pull request (lidge-jun#4058)