Skip to content

fix(launcher): retry uv install steps to survive transient first-run 448 (Windows/OneDrive)#325

Merged
lstein merged 2 commits into
masterfrom
lstein/fix/launcher-install-retry
Jun 9, 2026
Merged

fix(launcher): retry uv install steps to survive transient first-run 448 (Windows/OneDrive)#325
lstein merged 2 commits into
masterfrom
lstein/fix/launcher-install-retry

Conversation

@lstein

@lstein lstein commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Problem

On a Windows machine with OneDrive Files-On-Demand active, the very first run of the launcher aborted with:

error: Failed to create Python minor version link directory
  Caused by: ... untrusted mount point. (os error 448)

After extensive isolation (same uv binary, flags, env, folder, elevation, cwd, and environment all tested), the failure proved to be a transient first-run race: OneDrive's cloud-filter driver (cldflt) briefly returns STATUS_UNTRUSTED_MOUNT_POINT (448) on uv's managed-Python minor-version junction creation while the filter engages the freshly-created folder tree. Re-running the exact same uv command moments later — from bash, cmd, or even the Explorer shortcut — succeeds every time. uv has no flag to skip that junction (ensure_minor_version_link is unconditional and fatal in uv's source), so we can't avoid creating it; we can only survive a one-time failure.

Fix

Wrap the two uv install commands in a short, growing-backoff retry (installStepAttempts = 3, 3s then 6s). A one-time 448 self-heals on the next attempt instead of hard-aborting the user's first run — the moment it actually bites. Retrying is cheap: uv caches downloads, so a retry reuses the already-fetched Python/wheels. The retry logic is factored into a pure, unit-tested withRetry(attempts, backoff, fn) helper.

Also pass --no-bin to uv python install so uv stops dropping a python3.12.exe shim into the user's ~/.local/bin (which it can't manage on reinstall, and is one more cross-folder write outside our self-contained runtime root). The launcher runs start_photomap from the tool venv directly and never needs Python on PATH.

Tests

  • withRetry: recovers after transient failures, returns the last error when exhausted, stops on first success (no backoff). Pure helper — no real uv spawned.
  • go build, go vet, go test, gofmt -l all clean.

Notes

🤖 Generated with Claude Code

lstein and others added 2 commits June 9, 2026 15:03
On Windows with OneDrive Files-On-Demand, the cloud-filter driver (cldflt) can
transiently fail uv's creation of the managed-Python minor-version junction with
"untrusted mount point" (os error 448) while it engages a freshly created folder
tree on the very first run. The same uv binary, flags, env, and folder succeed
when run a moment later from a shell — it's a one-time race against the filter
settling, not a deterministic block, and uv has no flag to skip the junction.

Wrap the two uv install commands in a short, growing backoff retry
(installStepAttempts) so a one-time failure self-heals instead of hard-aborting
the user's first run — exactly when it bites. Retrying is cheap because uv
caches its downloads. The retry logic is factored into a pure, unit-tested
withRetry helper.

Also pass --no-bin to `uv python install`: the launcher runs start_photomap from
the tool venv directly, so there's no need for uv to drop a python3.12 shim into
~/.local/bin (which it can't manage on reinstall and is one more cross-folder
write outside our runtime root).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-install-retry

# Conflicts:
#	launcher/uv.go
@lstein lstein enabled auto-merge (squash) June 9, 2026 19:18
@lstein lstein merged commit 13a6619 into master Jun 9, 2026
9 checks passed
lstein added a commit that referenced this pull request Jun 9, 2026
…ython junction (#326)

The Windows/OneDrive first-run failure (os error 448, "untrusted mount point")
comes from uv creating the managed-Python *minor-version directory junction*.
OneDrive's cloud-filter driver (cldflt) blocks that junction creation on a
freshly-created folder tree; it is deterministic on a clean machine, so the
retry added in #325 cannot help (every attempt hits the same blocked junction).
uv has no flag to skip the junction.

Fix: don't let uv do a managed-Python *install* for the tool venv at all. We
still run `uv python install` to download+extract the interpreter, but we treat
a nonzero exit as success as long as the interpreter landed on disk (the junction
is the only thing that fails, and we don't need it). We then point `uv tool
install` at that interpreter via an explicit `--python <path>` — with
UV_PYTHON_INSTALL_DIR unset, so uv treats it as an external interpreter and never
attempts the junction. No junction is created, so the 448 cannot occur,
regardless of timing or OneDrive state. Verified by hand on the affected machine.

- uvEnv() no longer sets UV_PYTHON_INSTALL_DIR; ensurePython sets it only for the
  download step. uv's python-install output is captured and surfaced only if no
  usable interpreter is found, so the harmless 448 isn't shown.
- Removes the now-ineffective retry machinery (withRetry/runUVRetry) from #325.
- Keeps --no-bin (no python shim in ~/.local/bin) and #324's macOS Xcode warning.
- Adds a unit test for findExtractedPython; updates the uvEnv test for the new
  contract.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant