# How uv Extra Flags Resolve Dependency Conflicts When Installing PyTorch

> Learn how uv extra flags and pyproject.toml resolve PyTorch dependency conflicts by routing specific package indexes and preventing incompatible CUDA and CPU wheels.

- Repository: [Th3Unknovvn/uv-install-torch](https://github.com/baonguyen6742/uv-install-torch)
- Tags: tutorial
- Published: 2026-02-26

---

**The `uv` package manager uses PEP 508 extras combined with a `conflicts` table in [`pyproject.toml`](https://github.com/baonguyen6742/uv-install-torch/blob/main/pyproject.toml) to enforce mutually exclusive PyTorch variants, routing each extra flag to specific package indexes and preventing incompatible CUDA and CPU wheels from being installed simultaneously.**

When managing PyTorch installations, dependency conflicts between CPU-only and CUDA-enabled wheels can break Python environments. The `baonguyen6742/uv-install-torch` repository demonstrates how **uv extra flags resolve dependency conflicts** by leveraging optional dependency groups and explicit conflict declarations to isolate incompatible binaries.

## Declaring Mutually Exclusive Variants with Optional Dependencies

PyTorch distributes separate binary wheels for CPU-only and CUDA-enabled installations. To prevent these from colliding, the repository defines them as optional dependency groups that cannot coexist.

### Defining CPU and CUDA Extra Groups

In [`pyproject.toml`](https://github.com/baonguyen6742/uv-install-torch/blob/main/pyproject.toml), the repository declares two optional dependency groups under `[project.optional-dependencies]`:

```toml
[project.optional-dependencies]
cpu   = ["torch==2.4.1", "torchvision==0.19.1", "torchaudio==2.4.1"]
cu124 = ["torch==2.4.1", "torchvision==0.19.1", "torchaudio==2.4.1"]

```

Both groups contain identical package names and versions, but they resolve against different binary wheels depending on which extra flag is passed during installation.

### Enforcing Conflicts in pyproject.toml

To guarantee that both extras cannot be activated simultaneously, the repository includes a `conflicts` table in the `[tool.uv]` section:

```toml
[tool.uv]
conflicts = [[{ extra = "cpu" }, { extra = "cu124" }]]

```

> **Source:** [[`pyproject.toml`](https://github.com/baonguyen6742/uv-install-torch/blob/main/pyproject.toml) lines 30-33](https://github.com/baonguyen6742/uv-install-torch/blob/master/pyproject.toml#L30-L33)

This declaration instructs `uv` that the `cpu` and `cu124` extras are mutually exclusive. When the resolver encounters a request to install both, it aborts with a clear error message rather than attempting to merge incompatible wheel variants.

## Mapping uv Extra Flags to Package Indexes

Once the extras are defined, `uv` must know where to fetch the corresponding wheels. The repository uses the `sources` and `index` tables to route each extra to its specific PyTorch package index.

### Configuring Source-Specific Indexes

The `[tool.uv.sources]` table maps each package to different indexes based on the active extra:

```toml
[tool.uv.sources]
torch = [
  { index = "pytorch-cpu",  extra = "cpu"   },
  { index = "pytorch-cu124", extra = "cu124" }
]
torchvision = [
  { index = "pytorch-cpu",  extra = "cpu"   },
  { index = "pytorch-cu124", extra = "cu124" }
]
torchaudio = [
  { index = "pytorch-cpu",  extra = "cpu"   },
  { index = "pytorch-cu124", extra = "cu124" }
]

```

> **Source:** [[`pyproject.toml`](https://github.com/baonguyen6742/uv-install-torch/blob/main/pyproject.toml) lines 37-50](https://github.com/baonguyen6742/uv-install-torch/blob/master/pyproject.toml#L37-L50)

When `uv` resolves dependencies with `--extra cpu`, it selects the `pytorch-cpu` index for all three packages. When `--extra cu124` is passed, it switches to `pytorch-cu124`.

### Explicit Index Definitions for PyTorch Wheels

The indexes themselves are defined in the `[[tool.uv.index]]` array with the `explicit = true` flag, ensuring they are only used when explicitly requested via the sources table:

```toml
[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

[[tool.uv.index]]
name = "pytorch-cu124"
url = "https://download.pytorch.org/whl/cu124"
explicit = true

```

> **Source:** [[`pyproject.toml`](https://github.com/baonguyen6742/uv-install-torch/blob/main/pyproject.toml) lines 53-62](https://github.com/baonguyen6742/uv-install-torch/blob/master/pyproject.toml#L53-L62)

This configuration prevents `uv` from accidentally mixing wheels from different CUDA versions or CPU builds during resolution.

## How the uv Resolver Prevents Dependency Conflicts

The `uv` resolver evaluates the full dependency graph while respecting the constraints defined in [`pyproject.toml`](https://github.com/baonguyen6742/uv-install-torch/blob/main/pyproject.toml), ensuring that only one PyTorch variant exists in the final environment.

### Evaluating Extra-Specific Markers

During resolution, `uv` treats extras as conditional markers. The resolver activates only the dependency tree associated with the requested extra, ignoring the other. If both extras are requested simultaneously, the `conflicts` table triggers an immediate error:

```bash
uv sync --extra cu124 --extra cpu

```

```

error: Conflicting extras requested: 'cpu' and 'cu124' are mutually exclusive.

```

This prevents the installation of both `torch-2.4.1+cpu` and `torch-2.4.1+cu124` in the same environment, which would otherwise cause import errors and runtime conflicts.

### Lock File Generation with Conflict Isolation

The generated `uv.lock` file encodes the resolution result with markers that tie each package to its specific extra. For example, entries contain markers such as:

```

extra == 'extra-16-uv-install-torch-cpu'

```

or

```

extra == 'extra-16-uv-install-torch-cu124'

```

> **Source:** [`uv.lock` contains extra-specific markers throughout the file](https://github.com/baonguyen6742/uv-install-torch/blob/master/uv.lock)

These markers ensure that subsequent `uv sync` operations reproduce the exact same environment, and that the lock file remains valid only for the selected variant.

## Installing PyTorch with uv Extra Flags

To install PyTorch using the conflict-resolution configuration, specify the desired hardware variant using the `--extra` flag.

### Install the CUDA 12.4 Variant

To install PyTorch with CUDA 12.4 support, activate the `cu124` extra:

```bash
uv sync --extra cu124

```

This command resolves `torch`, `torchvision`, and `torchaudio` from the `pytorch-cu124` index and records the CUDA-enabled wheels in `uv.lock`.

### Install the CPU-Only Variant

For CPU-only installations, use the `cpu` extra:

```bash
uv sync --extra cpu

```

The resolver fetches packages from the `pytorch-cpu` index, ensuring no CUDA libraries are installed.

### Verify the Installation

After syncing, run the verification script to confirm the correct variant is active:

```bash
uv run main.py

```

When `cu124` is selected, the script confirms CUDA device availability. When `cpu` is selected, it verifies CPU-only operation without CUDA.

## Summary

- **Mutual exclusion via conflicts**: The `conflicts` table in [`pyproject.toml`](https://github.com/baonguyen6742/uv-install-torch/blob/main/pyproject.toml) explicitly declares that `cpu` and `cu124` extras cannot coexist, preventing installation of incompatible PyTorch wheels.
- **Index routing with sources**: The `[tool.uv.sources]` table maps each extra to a specific PyTorch index (`pytorch-cpu` or `pytorch-cu124`), ensuring the resolver fetches the correct binary variant.
- **Lock file isolation**: The generated `uv.lock` encodes extra-specific markers, guaranteeing reproducible environments that contain only one PyTorch variant.
- **Command-line activation**: Users activate the desired hardware target using `uv sync --extra cpu` or `uv sync --extra cu124`, with the resolver enforcing conflict rules at install time.

## Frequently Asked Questions

### What happens if I specify both `--extra cpu` and `--extra cu124`?

`uv` will abort with a clear error message stating that the extras are mutually exclusive. The `conflicts` table in [`pyproject.toml`](https://github.com/baonguyen6742/uv-install-torch/blob/main/pyproject.toml) triggers this error before any packages are downloaded, protecting your environment from containing both CPU and CUDA variants of PyTorch simultaneously.

### How does uv know which PyTorch wheel to download for each extra?

The `[tool.uv.sources]` table in [`pyproject.toml`](https://github.com/baonguyen6742/uv-install-torch/blob/main/pyproject.toml) explicitly maps each package (`torch`, `torchvision`, `torchaudio`) to a specific index based on the active extra. When you pass `--extra cu124`, `uv` consults this table and routes the download request to the `pytorch-cu124` index URL instead of the default PyPI.

### Can I add more CUDA versions using the same conflict resolution pattern?

Yes. You can define additional extras such as `cu118` or `cu121` in `[project.optional-dependencies]`, add corresponding entries to the `conflicts` table to ensure they are mutually exclusive with existing variants, and configure new explicit indexes in `[[tool.uv.index]]` mapped via `[tool.uv.sources]`.

### Where does uv store the information about which extra was selected?

The selection is recorded in the `uv.lock` file through extra-specific markers (e.g., `extra == 'extra-16-uv-install-torch-cu124'`). These markers ensure that subsequent `uv sync` operations reproduce the exact same dependency tree, and they prevent the lock file from being valid for conflicting extras simultaneously.