Skip to content

[Bug]: OpenCode update reports "still needs an update" after a successful upgrade because verification reads the running server version #13010

Description

@satyalyadav

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Enable the OpenCode provider with Server URL empty so T3 Code owns the local server.
  2. Open a thread and send a prompt so T3 Code spawns its opencode serve helper. Leave that helper running.
  3. When the update toast appears, click Update now.
  4. opencode upgrade runs, exits 0, and replaces the binary on disk.
  5. T3 Code shows Provider still needs an update / OpenCode still appears outdated even though the CLI on disk is now the latest version.

Expected behavior

After the update command exits 0, T3 Code should verify the newly installed version and show the success state. For a local OpenCode server that T3 Code owns, it should restart or invalidate that helper before re-reading the version, or fall back to the version from the opencode --version probe it just ran.

Actual behavior

The post-update verification reads the version from the already-running opencode serve child instead of the updated CLI. opencode upgrade replaces the binary but the running process keeps the old code in memory, so /global/health keeps returning the old version. The advisory stays behind_latest, the update state becomes unchanged, and the client renders Provider still needs an update.

Relevant code on main:

  • apps/server/src/provider/Layers/OpenCodeProvider.ts probes opencode --version, then overwrites that result with the server handle's version at line 535: version = inventoryExit.value.version;
  • The local server handle comes from OpenCodeServerOwner.withServer(...), which reuses any live server and only restarts it after OPENCODE_SERVER_IDLE_TTL = "30 seconds" (apps/server/src/provider/OpenCodeServerOwner.ts).
  • apps/server/src/provider/providerMaintenanceRunner.ts computes stillOutdated from that refreshed snapshot and records status: "unchanged" at line 416, which produces the toast.

Timeline from my machine (local time, 2026-09-21, OpenCode 1.18.31 to 1.18.32, native install, no npm):

  • 22:16:30 the update starts. opencode upgrade exits 0 after about 15 s and the binary on disk becomes 1.18.32.
  • 22:16:45.667 the provider probe records version = 1.18.31 in ~/.t3/caches/opencode.json, even though opencode --version reports 1.18.32 at the same moment.
  • 22:16:49.99 the update finishes as unchanged.
  • 22:17:39 and 22:18:26 new opencode serve children start. Both report 1.18.32 on /global/health, so the reported state self-corrects once the helper is replaced.

Trace: ProviderMaintenanceRunner.runCommandAndVerify, traceId b345808a7791d2cd9d02b630872e0cbc.

Note: this is the same visible symptom as #5629 and #3550 (Codex) and #8280 (Claude Code), but a different root cause. The binary install path is detected correctly here, the upgrade succeeds, and the CLI probe returns the new version. Only the server version used for verification is stale.

Impact

Minor bug or occasional failure

Version or commit

T3 Code 0.0.42 (client APP_VERSION from the running bundle)

Environment

Windows host with a WSL2 (Ubuntu) environment. OpenCode provider enabled with the default local server (Server URL empty). OpenCode upgraded from 1.18.31 to 1.18.32 via opencode upgrade, native install at ~/.opencode/bin/opencode.

Logs or stack traces

# Snapshot written by the failed verification (~/.t3/caches/opencode.json)
{
  "version": "1.18.31",
  "checkedAt": "2026-09-22T05:16:45.667Z",
  "versionAdvisory": {
    "status": "behind_latest",
    "currentVersion": "1.18.31",
    "latestVersion": "1.18.32",
    "updateCommand": "~/.local/bin/opencode upgrade",
    "canUpdate": true
  }
}

# CLI after the update
$ opencode --version
1.18.32

# T3 Code owned servers after the helper was replaced
$ curl -s http://127.0.0.1:<port>/global/health
{"healthy":true,"version":"1.18.32"}
Span timeline (server.trace.ndjson):
22:16:30  ProviderMaintenanceRunner.updateProvider started
22:16:45  ProviderMaintenanceRunner.collectCommandResult finished (exit 0, 14.9 s)
22:16:45  checkOpenCodeProviderStatus started, recorded version 1.18.31
22:16:50  setProviderMaintenanceActionState status=unchanged

Screenshots, recordings, or supporting files

Toast text: Provider still needs an update / OpenCode still appears outdated. Check provider settings for details. with a Settings button.

Workaround

Refresh provider status after the local helper has been idle for about 30 seconds (it restarts from the new binary), or restart T3 Code. The update itself is already applied.

Activity

  1. juliusmarminge commented on Sep 22, 2026

    @juliusmarminge
    Member

    Triage

    Confirmed on main at 5a61f50cc. This is not a duplicate of #5629, #3550, or #8280. Those share the "Provider still needs an update" toast, but the update command there did not actually update the binary T3 Code later probed. Here opencode upgrade is the right command, it exits 0, and opencode --version is already the new build. Only the version used for verification is stale.

    For a local OpenCode install (Server URL empty), post-update verification calls refreshInstance, which runs checkOpenCodeProviderStatus:

    1. opencode --version starts a new process and parses the on-disk version (OpenCodeProvider.ts around the --version probe).
    2. Inventory is loaded through OpenCodeServerOwner.withServer. If the owned child is still running, that handle is reused. It is only closed after OPENCODE_SERVER_IDLE_TTL ("30 seconds") once nothing is borrowing it, and the verification probe itself is a borrower, so it resets that timer. A live turn keeps the old process until the turn ends and the idle timeout fires (OpenCodeServerOwner.ts).
    3. The handle's version is whatever /global/health returned when that process was spawned (verifyOpenCodeServerVersion in startOpenCodeServerProcess). It is not read again on the next borrow. opencode upgrade replaces the binary; the running process keeps the old code.
    4. On a successful inventory, that startup version overwrites the CLI probe (version = inventoryExit.value.version). The snapshot, ~/.t3/caches/opencode.json, and the version advisory all follow the old server. behind_latest makes ProviderMaintenanceRunner record status: "unchanged", and the client shows "Provider still needs an update" / "OpenCode still appears outdated."

    That overwrite came from #8480, which started reporting the serving process instead of the CLI. That matches an external server, and it matches a local server at spawn time. It is wrong for the window after a successful upgrade of a T3-owned helper.

    The native update path is doing the right thing on this install. isOpenCodeNativeCommandPath matches a real path under ~/.opencode/bin/opencode, and the command that runs is the resolved binary plus upgrade (OpenCodeDriver.ts). The cached ~/.local/bin/opencode upgrade is that branch, not the old npm guess. #9325 (which closed #5629) only selects the installer that owns the binary. It does not restart or ignore a stale owned opencode serve. #3550 was closed not_planned on a Codex/Bun hypothesis. #8280 was a Claude/Homebrew hang where the suggested upgrade was already a no-op. None of those change this path.

    The reported self-heal matches the owner: once the old child is idle long enough to be replaced, the next health check is the new binary and the advisory clears. Refreshing after that idle period, or restarting T3 Code, is a valid workaround. The install is already updated.

    A fix should not kill a helper that a turn is still borrowing. After opencode upgrade exits 0, verification for a T3-owned local server should treat the CLI probe as the installed version when it is newer than the cached server version, or restart the owned server only when this probe is the sole borrower and then re-read /global/health. An in-flight turn can keep the old process until it goes idle.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Sep 22, 2026
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 is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions