Skip to content

[Bug]: mise upgrades can strand systemd on removed versioned OpenCodex paths #2898

Closed

Description

@mushikingh

Client or integration

Codex CLI

Area

Service lifecycle

Summary

The Linux systemd user unit generated by ocx service install embeds the absolute, versioned OpenCodex CLI and bundled Bun paths. When OpenCodex is installed through mise, mise upgrade opencodex installs the next release under a different version directory without running OpenCodex's own update workflow, so the installed unit remains tied to the previous version.

Once mise removes the previous install or the service is restarted after pruning, systemd has no executable at the baked path and the background proxy cannot start. ocx service repair fixes the unit, but it has to be run manually after every external package-manager upgrade.

I currently need this mise configuration as a local workaround:

opencodex = { version = "latest", postinstall = 'if test -f "$HOME/.config/systemd/user/opencodex-proxy.service"; then "$MISE_TOOL_INSTALL_PATH/node_modules/.bin/ocx" service repair; fi' }

The guard makes a first installation without a service a no-op and forces an existing service to be re-baked after mise changes the install path.

Expected: an installed OpenCodex service remains startable across supported external package-manager upgrades, or OpenCodex provides a package-manager-independent launcher/service path that does not require a per-manager postinstall repair hook.

Actual: the service definition is coupled to one mise version directory and becomes stale when that directory is replaced or pruned.

Reproduction

  1. Install OpenCodex through mise:

    mise use -g opencodex@latest
  2. Install the Linux user service:

    ocx service install
  3. Inspect the generated unit:

    systemctl --user cat opencodex-proxy.service | grep -E '^(ExecStart|Environment="OCX_BUN_RUNTIME_PATH)='

    Both the bundled Bun runtime and CLI entry point are under a version-specific path such as:

    /home/<user>/.local/share/mise/installs/opencodex/2.35.0/...
    
  4. Upgrade to the next OpenCodex release through mise:

    mise upgrade opencodex
  5. Confirm the unit still names the previous version directory. After that old install is removed or pruned, restart the user service or reboot:

    systemctl --user restart opencodex-proxy.service
    ocx status
  6. Repairing manually re-bakes the unit and restores service health:

    ocx service repair
    ocx status

This is distinct from #2411 and #2412. Their released fixes improve routing visibility and correctly reject a version-manager-owned Codex shim, but they do not make the OpenCodex systemd service survive an external OpenCodex package upgrade.

Version

2.35.0

Operating system

Ubuntu 24.04.4 LTS on WSL2, kernel 6.18.33.2-microsoft-standard-WSL2

Provider and model

Not provider- or model-specific.

Logs or error output

$ systemctl --user show opencodex-proxy.service -p ActiveState -p UnitFileState
ActiveState=active
UnitFileState=enabled

$ systemctl --user cat opencodex-proxy.service
# ExecStart and OCX_BUN_RUNTIME_PATH both contain:
/home/<user>/.local/share/mise/installs/opencodex/2.35.0/...

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. added
    bugSomething isn't working
    serviceService lifecycle (WinSW/launchd/scheduler)
    on Aug 29, 2026
  2. lidge-jun commented on Aug 29, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 62 / 80

    설명

    Linux에서 ocx service install이 만든 systemd user 유닛이 버전 경로가 박힌 CLI/Bun ExecStart를 쓰고, mise로 mise upgrade opencodex하면 설치 디렉터리가 바뀌는데 유닛은 예전 경로를 남는다는 버그입니다. 이전 버전이 지워지거나 재시작되면 systemd가 실행 파일을 못 찾아 백그라운드 프록시가 안 뜹니다. ocx service repair로 다시 구우면 고치지만, 외부 패키지 매니저 업그레이드마다 수동 실행이 필요합니다.

    현재 dev에는 이미 stale 감지 문구가 있습니다. src/service.ts 근처의 STALE baked paths (missing: ...) — run 'ocx service repair' to re-bake(약 2513행)와 #2869로 올라온 systemd 219 호환 정리, #2861의 staleProcessState 표면화가 같은 가족입니다. 다만 보고된 구멍은 "이미 죽었을 때 진단"이 아니라, mise처럼 OpenCodex 업데이트 워크플로를 거치지 않는 외부 업그레이드 후 자동으로 repair가 안 도는 것입니다. 작성자의 mise postinstall 워크어라운드가 그 갭을 정확히 보여 줍니다.

    기대 동작은 (1) 버전드 절대경로 대신 안정적인 shim/wrapper를 ExecStart에 쓰거나, (2) 업그레이드/시작 시 베이크된 경로가 사라졌으면 자동 repair, (3) 최소한 doctor/status가 mise/외부 업그레이드 시나리오를 명시하는 것입니다. src/update/index.ts는 자체 업데이트 경로에서 ocx service repair를 안내하지만, mise postinstall은 그 경로를 타지 않습니다.

    이 문제는 Windows tray/scheduler 계열(#2856/#2857)과는 플랫폼이 다르고, types/config 분할과도 무관합니다. 서비스 수명주기 버그로 dev에 독립 착륙시키기 좋은 후보입니다. 우선순위는 "프록시가 안 떠서 전체가 멈춤"이라 체감이 큽니다.

    경로 src/service.ts ExecStart 생성 - 절대 버전 경로를 굽는 설계가 mise/nix/asdf 같은 버전드 prefix와 충돌합니다. shim(~/.local/bin/ocx)이나 $MISE_TOOL_INSTALL_PATH가 아닌 안정 링크를 쓸지 설계가 필요합니다.
    경로 STALE baked paths 진단 - 이미 탐지는 있지만, 실패 시점이 서비스 start 이후라 사용자가 먼저 장애를 겪습니다. install/ensure/start 초기에 경로 existence 검사 후 자동 repair 후보입니다.
    경로 src/update - 자체 업데이트만 repair를 안내하고 외부 패키지 매니저는 비어 있습니다. 문서에 mise postinstall 예시를 공식화할지, 코드로 훅을 넣을지 결정이 필요합니다.
    심볼 ocx service repair - 해결책은 이미 있는데 트리거가 수동입니다. "도구 없음"이 아니라 "자동화 구멍"입니다.

    메인테이너의 판단이 필요한 지점

    • ExecStart를 안정 shim으로 바꿀지, 버전 경로를 유지한 채 자동 repair만 강화할지
    • ocx ensure/ocx start가 stale baked path면 자동 repair를 돌릴지(부작용: 의도치 않은 유닛 재작성)
    • mise/asdf 공식 문서 섹션을 추가할지
    • fix(cli): report an unclean prior proxy exit instead of a silent outage #2861 staleProcessState와 한 PR로 묶을지 별도 유지할지

    너의 추천
    버그 라벨 유지하고 받아 두세요. 추천 방향은 (A) ExecStart를 가능한 한 안정적인 ocx shim으로 재베이크 + (B) start/ensure 시 missing baked binary면 repair 제안 또는 자동 repair입니다. 문서에 작성자 mise postinstall 예시를 당분간 공식 워크어라운드로 적어도 좋습니다. 닫지 마세요.

    이 댓글은 grok-bot이 작성했습니다

  3. lidge-jun commented on Aug 29, 2026

    @lidge-jun
    Owner

    Fixed on dev in 4ec1cc9 (#2909).

    Your diagnosis was right, and the mechanism is worth stating precisely because it explains
    why the service looked installed the whole time. Both paths baked into the unit — the bundled
    Bun and the CLI entry — are derived from import.meta.dir, so both live inside the versioned
    install directory. Once mise removes it, /bin/sh still starts, exec <old-bun> <old-cli>
    fails, and Restart=on-failure restart-loops. startSystemd only checked unit existence and
    daemon-reload state, so nothing on the start path noticed.

    The unit now invokes the stable ocx executable resolved from PATH at install time. A mise
    or asdf shim survives the upgrade and dispatches to whatever version is current. Two details
    that mattered:

    • The resolution is lexical. Calling realpath on the shim would write the versioned
      target straight back into the unit and recreate the bug — the indirection is the point.
    • Only an absolute path is accepted, so ExecStart never re-resolves a bare ocx through
      PATH on each restart.

    OCX_BUN_RUNTIME_PATH is also dropped in launcher mode, since stamping it would pin the
    runtime to the same directory the upgrade deletes. Source checkouts without an ocx launcher
    keep the previous direct Bun + CLI form.

    A second half was needed that the report did not mention: the install state now records the
    launcher, and the staleness diagnostic judges against it. Without that, a perfectly healthy
    service reports STALE baked paths after every upgrade, because the recorded Bun/CLI pair
    points into the replaced directory.

    One action needed for existing installs. A unit written before this release still carries
    the old versioned paths and cannot migrate itself — once the old executable is deleted there
    is no OpenCodex code left running to fix it. Run ocx service repair once after upgrading;
    version changes after that need nothing.

    Worth noting about verification: no live systemd host was exercised. The regression tests
    build a real shim, point it at v1, retarget it to v2, delete v1, and execute the generated
    command — but that is a shell-level proof, not a running systemd unit. If you can confirm on
    your WSL setup after the next release, that would close the loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingserviceService lifecycle (WinSW/launchd/scheduler)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions