Skip to content

fix(server): stop offering Codex updates it cannot run and show why updates fail - #22505

Closed
fixfon wants to merge 2 commits into
pingdotgg:mainfrom
fixfon:fix/22487-codex-update-manual-only
Closed

fixfon wants to merge 2 commits into
pingdotgg:mainfrom
fixfon:fix/22487-codex-update-manual-only

Conversation

@fixfon

@fixfon fixfon commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Closes #22487

Problem

Two parts, both accepted in triage:

  • Update button that can never succeed. Codex recognizes a standalone install only when its path contains /packages/standalone/. Any other path that no installer is proven for falls back to codex update. A root-owned system install such as /usr/local/bin/codex -> /usr/local/lib/codex/releases/... therefore gets an Update button, but codex update exits 1 on installs it can't identify. On main the resolver returns /usr/local/bin/codex update for that path.
  • The toast hides the reason. failureMessage returns only Update command exited with code N.. The updater's stderr and stdout are stored in updateState.output, but the clients render updateState.message, so the user never sees why the update failed.

Change

  • packages/provider-core/src/server/maintenanceResolver.ts: a new opt-in manualWhenUnproven definition option. When it is set, the final "nothing proven" return gives the manual result (with installPath) instead of the native-updater fallback.
  • apps/server/src/provider/Drivers/CodexDriver.ts: Codex sets manualWhenUnproven: true. Standalone installs and installs with a proven installer (npm, pnpm, Bun, Yarn, Homebrew, Vite+, ...) keep their update command. A Homebrew-shaped install whose brew can't be confirmed still gets codex update, as before.
  • apps/server/src/provider/providerMaintenanceRunner.ts: a non-zero exit now reads Update command exited with code N: <excerpt>. The excerpt is the last two non-empty lines of stderr, or of stdout when stderr is empty, passed through the existing toProgressLine sanitizer (strips terminal control sequences, collapses whitespace, caps a line at 200 characters). Empty output keeps the old message. Timeout and unknown-exit messages are unchanged, and so is the output field.
  • docs/internals/providers.md and docs/user/install.md: both said the provider's own updater runs when nothing is proven, which is no longer true for Codex.

Only Codex turns the option on, so Claude, OpenCode and Pi still fall back to their own updaters. The "exits 1 on unrecognized installs" behavior is only reported for Codex, and the other updaters are documented to detect their installer themselves. Two lines instead of one because npm's last line is just a log-file pointer. If you'd rather make unproven installs manual-only for every provider, as the triage note puts it, that's a one-line change and I can switch it.

Verification

  • New maintenanceResolver.unprovenInstall.test.ts: on main the unproven-install case fails (receives /usr/local/bin/codex update); the standalone and npm-global guards pass on both.
  • CodexDriver.test.ts: a direct /usr/local/lib/codex/releases/.../bin/codex path and a /usr/local/bin/codex symlink both stay manual-only. They fail on main. A linked standalone release still gets codex update with CODEX_HOME.
  • New providerMaintenanceFailureMessage.test.ts: stderr shown, stdout fallback, stderr preferred, ANSI stripped, 500 lines shows only the last two, a 10 KB line stays bounded, empty or whitespace-only output keeps the exit-code message. Six of these fail on main.
  • Three more tests in providerMaintenanceFailureMessage.test.ts run a failing update: the final error of output longer than 10 KB, a cut first line of a long output, and a long line with no newline. They fail on the previous commit (809912c) and pass with 263a922.
  • One existing assertion in providerMaintenanceRunner.test.ts now expects Update command exited with code 1: permission denied.
  • apps/server (CodexDriver, providerMaintenanceFailureMessage, providerMaintenanceRunner): 54/54 pass. packages/provider-core maintenanceResolver suites: 65/65 pass. With the three production files reverted to main, 13 of the new or updated tests fail.
  • vp fmt --check is clean, vp lint on the touched files exits 0, and typecheck passes for packages/provider-core and apps/server.
  • Not checked: running codex update against a real system-wide install (it could modify the install), and a pass in the app UI. No client code changed; clients already render message.

Claude Sonnet 5.5 (xhigh) via T3 Code.

…pdates fail

An unrecognized Codex install path (for example a root-owned system copy under
/usr/local/lib/codex) fell back to `codex update`, which exits 1 on any install
it cannot identify, so the Update button could never succeed. Codex now stays
manual-only unless its path is a standalone install or an installer T3 Code
proves (npm, pnpm, bun, yarn, Homebrew, Vite+, ...). The new
`manualWhenUnproven` definition option is opt-in, so Claude, OpenCode and Pi
keep falling back to their own updaters.

A failed update's message was only "Update command exited with code N.", which
is all the toast shows. It now appends the last two non-empty lines of the
updater's output (stderr first, then stdout), run through the same
toProgressLine sanitizer that already shortens installer output for the running
state, so the toast stays short and free of terminal control sequences.

Fixes pingdotgg#22487
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 20, 2026
Comment thread apps/server/src/provider/providerMaintenanceRunner.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Would Approve

Macroscope's review found this PR approvable — This is a bounded server-side bug fix that removes an unusable Codex update action for unrecognized installs and surfaces sanitized updater errors, with targeted regression coverage and no schema, security, or deployment impact. A capped-output edge case may make an excerpt less precise, but it does not change the limited scope of the fix.

Needs human approval because:

  • 1 blocking correctness issue found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f0b43be9-91eb-4fe0-bb27-51eba100af31
📥 Commits

Reviewing files that changed from the base of the PR and between 809912c and 263a922.

📒 Files selected for processing: 2
  • apps/server/src/provider/providerMaintenanceFailureMessage.test.ts
  • apps/server/src/provider/providerMaintenanceRunner.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

Codex installations with unproven ownership now require manual updates. Failed update messages include a bounded excerpt of command output when available.

Changes: Provider maintenance

Layer / File(s) Summary
Codex maintenance resolution
packages/provider-core/src/server/maintenanceResolver.ts, apps/server/src/provider/Drivers/CodexDriver.ts, packages/provider-core/src/server/maintenanceResolver.unprovenInstall.test.ts, apps/server/src/provider/Drivers/CodexDriver.test.ts, docs/internals/providers.md, docs/user/install.md
The resolver supports manual-only maintenance when installation ownership is unproven. Codex uses this setting. Tests cover system, standalone, and npm-global installations. Documentation describes the update behavior.
Update failure messages
apps/server/src/provider/providerMaintenanceRunner.ts, apps/server/src/provider/providerMaintenanceRunner.test.ts, apps/server/src/provider/providerMaintenanceFailureMessage.test.ts
Nonzero-exit failure messages include up to two normalized lines from stderr, or stdout when stderr is empty. Captured tails remain bounded. Tests cover output selection, terminal-control stripping, truncation, and timeout and unknown-exit messages.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Low

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 263a9

No actionable issue is established for this change; it is ready for normal merge checks.

Security architecture risk: 🔵 Low · up to 263a9

The change narrows automatic updates and bounds the failure text shown to users. No new execution authority was identified. Remaining uncertainty concerns potentially sensitive information in updater diagnostics, which are now displayed more prominently.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The command-policy change narrows Codex update eligibility. The diagnostic change has broader scope because all providers using the shared runner can place updater-supplied failure text into existing provider-state messages. No additional executable authority is introduced by these changes.

Trust Boundaries and Controls

  • observed — The existing provider-state transport already exposed bounded updater output before this PR. The new excerpt changes presentation and retains final lines beyond the earlier output-head capture, but uses the same maintenance-state publication path rather than a new service or recipient boundary.

Hardening Proposals

  • proposed — Validate representative updater failure diagnostics for credentials or credential-bearing URLs. If such content occurs, redact it before publishing progress, stored output and failure messages; terminal sanitization alone does not provide that protection.
Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly summarizes both primary changes: preventing unsupported Codex updates and showing update failure reasons.
Linked Issues check Passed Issue [#22487] has two coding requirements. Codex sets manualWhenUnproven: true, and the resolver returns manual-only capabilities with installPath when no installer is proven. Proven standalone a…
Out of Scope Changes check Passed The changes stay within issue [#22487]. The resolver option supports the Codex manual-update behavior without changing other providers. Tail capture supports the required failure excerpt and preserves…
Description check Passed The description covers the problem, implementation, scope, verification results, limitations, and agent details. It references issue #22487 and states that the changes were accepted in triage, but it …
Full details: Linked Issues check

Explanation

Issue [#22487] has two coding requirements. Codex sets manualWhenUnproven: true, and the resolver returns manual-only capabilities with installPath when no installer is proven. Proven standalone and package-manager installs retain update commands. The maintenance runner adds the last two non-empty sanitized lines from stderr, or stdout when stderr is empty, to non-zero exit messages. Empty output, timeout handling, and the existing output field remain unchanged. Focused tests cover the resolver, Codex paths, output selection, sanitization, bounds, and failure behavior.

Full details: Out of Scope Changes check

Explanation

The changes stay within issue [#22487]. The resolver option supports the Codex manual-update behavior without changing other providers. Tail capture supports the required failure excerpt and preserves the existing output field. Tests and documentation cover the changed behavior. No unrelated production behavior is demonstrated.

Full details: Description check

Explanation

The description covers the problem, implementation, scope, verification results, limitations, and agent details. It references issue #22487 and states that the changes were accepted in triage, but it does not include a direct maintainer approval comment as requested by the template.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

The output capture keeps the first 10 KB, so for an updater that wrote more
than that, the failure message's last-lines excerpt came from the middle of its
progress text, not its real final error. The runner now also keeps the last
4 KB of each stream while it is read and the excerpt uses that. The
`output` field and its 10 KB cap are unchanged.
@fixfon

fixfon commented Oct 22, 2026

Copy link
Copy Markdown
Contributor Author

Closing this since #22487 was fixed by #22506. Thanks!

@fixfon fixfon closed this Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Codex update always fails on a remote host with a root-owned system-wide Codex install, and the toast hides the reason

1 participant