Skip to content

[Bug]: Turning another provider on/off waits until a running provider update finishes #26062

Closed

Description

@jlipworth

What happened

While one provider is running "Update now" (e.g. a slow Muse update), turning a different provider (Claude, Codex, …) on or off in Settings → Providers does nothing until that update finishes. The switch doesn't move, then the toggle applies late once the update completes.

It isn't limited to the switch: every settings write to that environment waits behind the running provider update.

Steps to reproduce

  1. Settings → Providers, click "Update now" on a provider whose update takes a while.
  2. While it is still running, toggle a different provider off.

Expected: the toggle applies immediately; only the provider being updated is busy.
Actual: the toggle (and any other settings change, keybinding edit, add/remove instance) is held until the update RPC resolves.

Cause

serverEnvironment.updateProvider runs on the same client-side serial lane as all settings writes:

  • packages/client-runtime/src/state/server.ts:758-761: configConcurrency = { mode: "serial", key: environmentId }
  • server.ts:1143-1148: updateSettings uses configScheduler + configConcurrency
  • server.ts:1390: mutateProviderInstance: updateSettings (what the provider enable switch calls via usePersistEnvironmentProviderInstanceMutation)
  • server.ts:1370-1375: updateProvider also uses configScheduler + configConcurrency
  • packages/client-runtime/src/state/runtime.ts:246-249: serial mode chains each command behind the previous promise for the key

The server's serverUpdateProvider handler (apps/server/src/ws.ts:2464 → ProviderMaintenanceRunner.updateProvider) only resolves after the install command, version verification and refreshAfterUpdate, so the update holds the environment's config lane for its whole duration. The switch's checked state comes from persisted settings (apps/web/src/components/settings/ProviderInstanceCard.tsx:1034-1038), so it appears stuck.

Nothing below the client blocks: the switch isn't disabled for other providers' updates, and on the server ProviderInstanceRegistry reconcile and ProviderRegistry.syncLiveSources don't contend with the update's lock.

Side effect: "Update all" (apps/web/src/components/ProviderUpdatesAction.tsx) is also serialized client-side, on top of the server's own per-lock-key queue.

Since the lane lives in packages/client-runtime, this affects web, desktop and mobile.

Suggested fix

Take updateProvider off the config lane. The server already serializes updates per install via providerMaintenanceCommandCoordinator (and reports the "Waiting for another provider update to finish" queued state), so the client queue adds nothing. Either:

  • mirror updateServer, which already has its own updateScheduler (server.ts:757, 831), or
  • use concurrency: { mode: "singleFlight", key: ({ environmentId, input }) => JSON.stringify([environmentId, input.instanceId]) }.

A focused client-runtime test could assert that an updateSettings call resolves while an updateProvider call is still pending.

Related but distinct: #24998 (update state stuck at running server-side).

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions