Open-Source Wikis

/

uv

/

How to contribute

/

Debugging

astral-sh/uv

Debugging

uv is heavily instrumented with tracing. Most of what you'll need to debug a behavior is already there, you just need to switch on the right level of logging.

Verbose output

The user-visible flag is -v / --verbose:

Flag What you get
(none) info-level user-visible messages only
-v debug for uv* crates
-vv trace for uv* crates
-vvv trace for all crates

These levels are wired up in crates/uv/src/lib.rs (search for Level::DebugUv / Level::TraceUv) and crates/uv/src/logging.rs. The actual tracing filter strings are built from RUST_LOG and these flags.

You can also set RUST_LOG directly to override:

$ RUST_LOG=trace cargo run -- lock
$ RUST_LOG=uv_resolver=trace,uv_client=debug cargo run -- pip compile requirements.in

Profile and span timing

For performance debugging, build with the tracing-durations-export feature:

$ RUST_LOG=uv=info \
  TRACING_DURATIONS_FILE=target/traces/jupyter.ndjson \
  cargo run --features tracing-durations-export --profile profiling -- pip compile test/requirements/jupyter.in

Then visualize with tracing-durations-export. Each span in the codebase (#[instrument]-annotated functions) becomes a row, so you can see where time goes during resolution, prefetching, downloads, and installs.

Showing resolved settings

--show-settings (hidden flag) prints uv's view of the resolved configuration for the current command:

$ cargo run -- --show-settings sync

This dumps the merged settings struct (defined per command in crates/uv/src/settings.rs) so you can see how config files, environment variables, and CLI flags combined.

Common errors

"No solution found"

The resolver formats its error in crates/uv-resolver/src/error.rs (NoSolutionError::Display). It includes a derivation chain explaining why a particular set of constraints is unsatisfiable. To see more detail, run with -v to get the resolver's debug! traces — every fork, every backtrack, every conflict gets logged.

If you're debugging the resolver itself, add tracing::debug! or tracing::trace! calls inside crates/uv-resolver/src/resolver/mod.rs and rerun with RUST_LOG=uv_resolver=trace.

"No interpreter found"

Discovery errors are produced by crates/uv-python/src/discovery.rs::Error and surface as PythonNotFound. Setting -v triggers debug! and trace! lines that show every candidate interpreter the discovery loop considered, where it looked, and why each one was rejected (version mismatch, broken symlink, wrong implementation, …).

"Failed to fetch metadata"

The HTTP layer logs requests and responses at debug and trace. In particular, crates/uv-client/src/registry_client.rs logs every request URL, response code, and cache hit / miss. The linehaul module (crates/uv-client/src/linehaul.rs) is the User-Agent that PyPI logs. If a registry is rejecting requests, look there first.

TLS errors

TLS errors are produced by crates/uv-client/src/tls.rs and crates/uv-auth/src/middleware.rs. The error messages distinguish between certificate validation failures, hostname mismatches, and unsupported cipher suites. --allow-insecure-host (alias --trusted-host) bypasses TLS for a specific host (with a clear security warning in STYLE.md and the CLI help).

--native-tls and UV_NATIVE_TLS are deprecated as of #18705 (Apr 2026). The default is rustls + webpki-root-certs.

Cache corruption

Each cache bucket has a version (ARCHIVE_VERSION, the Vn suffix in crates/uv-cache/src/lib.rs::CacheBucket::to_str). Corrupt entries should be invalidated by bumping the bucket version. As a quick local fix you can delete the cache:

$ uv cache clean
$ rm -rf ~/.cache/uv  # nuclear option

The cache lives at $UV_CACHE_DIR if set, otherwise ~/.cache/uv on Linux, ~/Library/Caches/uv on macOS, and %LOCALAPPDATA%\uv\cache on Windows.

Inspecting state

The lockfile

uv.lock is a TOML file. The LockVersion constant in crates/uv-resolver/src/lock/mod.rs controls forward-compatibility. When debugging a lockfile issue, the easiest path is:

  1. Reproduce by running the command with -v.
  2. Diff the resulting uv.lock against the previous one.
  3. Look at crates/uv-resolver/src/lock/mod.rs for the read/write code.

The cache directory

uv cache dir prints the cache root. Subdirectories are versioned per-bucket: simple-v18, wheels-v6, archive-v0, etc. Each bucket has its own layout documented in cache/mod.rs.

The Python install dir

uv python dir prints the managed-Python install root. Each install is laid out as <implementation>-<version>-<os>-<arch>-<libc>/install/.... A download-metadata.json cache inside crates/uv-python/ describes every supported build.

The tool dir

uv tool dir prints where per-tool environments live. Each contains the venv plus a uv-receipt.toml describing what was installed.

Debugger workflows

gdb and lldb work well against a debug build. To break inside the resolver:

$ rust-gdb target/debug/uv -- lock
(gdb) b uv_resolver::resolver::ResolverState::next_iteration
(gdb) run

For more interactive exploration, tokio-console works against a uv process built with the tokio_unstable cfg flag and the console-subscriber dependency added to crates/uv — neither is enabled by default.

When all else fails

  • File an issue with the full -vvv output and uv --version.
  • Include the relevant pyproject.toml, uv.lock, and the smallest reproducing command.
  • For platform-specific issues (Windows registry, macOS dylibs, Linux musl), include the platform information uv prints under uv version and the relevant Python interpreter info from uv python list.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Debugging – uv wiki | Factory