Skip to content

Self-update (auto-update on startup and kbagent update) can corrupt the uv tool venv on Windows — non-atomic in-place upgrade of the running venv #528

Description

@papousek-radan

Summary

kbagent's self-update mechanism — both the automatic Updating kbagent vX -> vY... on startup and the explicit kbagent update — can leave the uv tool venv in a corrupted, inconsistent state on Windows. The update swaps packages in-place inside the venv the running kbagent.exe was launched from, so any mid-flight failure (network error, locked file) strands the venv between versions and every subsequent invocation crashes.

Observed twice in two days on the same machine.

Environment

  • Windows 11 Enterprise (10.0.26200)
  • kbagent installed via uv tool install "keboola-cli[server] @ <release wheel URL>", Python 3.14
  • kbagent versions involved: 0.72.0 → 0.75.0 (incident 1), 0.75.0 → 0.76.1 (incident 2)

Incident 1 — auto-update on startup (2026-07-22, 0.72.0 → 0.75.0)

On startup kbagent printed Updating kbagent v0.72.0 -> 0.75.0..., the update failed but kbagent continued. Afterwards the venv had mismatched rich/typer versions, and every subsequent invocation failed with:

ModuleNotFoundError: No module named 'rich._windows'

and

ImportError: cannot import name 'rich_utils' from 'typer'

Incident 2 — explicit kbagent update (2026-07-23, 0.75.0 → 0.76.1)

kbagent update crashed mid-update:

version_service.self_update → _update_mcp → httpx create_ssl_context → FileNotFoundError

The FileNotFoundError was for certifi's CA bundle file — the old dist had already been partially removed from site-packages while the still-running process referenced it, so when the post-update MCP version check tried to create an SSL context, certifi's cacert.pem no longer existed on disk. The venv was left broken between versions.

Root cause (as far as we can tell)

The self-update replaces site-packages / the entrypoint in-place, inside the venv that the running kbagent.exe was launched from:

  • On Windows the running exe is locked, so the upgrade fails with os error 32 if e.g. kbagent serve is running (that part is expected and correctly reported).
  • But even without an exe lock, the dependency swap is not atomic. If anything fails mid-flight — a network error in the MCP-update step, a locked file — the venv is stranded with a mix of old and new dists, and there is no rollback.
  • Incident 2 shows the ordering problem specifically: files were being removed/replaced before the network-dependent MCP latest-version check ran, so a failure in that check left the swap half-done.

Recovery that works

uv tool install --force "keboola-cli[server] @ <release wheel URL>"

fully rebuilds the venv and fixes both incidents.

Suggested fixes

  1. Perform self-update via uv tool install --force (or equivalent) into a fresh resolution rather than an in-place pip-style upgrade of the live venv — a failure then leaves the old install untouched.
  2. Alternatively, spawn a detached updater process that runs after the CLI exits, so the update never mutates the venv of a running process.
  3. Independently of 1/2: fetch the MCP-server latest-version check (and any other network calls) before touching any files, so a network failure can't interrupt the swap.

Context

Same reporter/setup as #522 (the cp1250 kbagent serve startup crash, fixed in #526 / v0.76.1) — happy to provide more logs or test a fix.

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