---
title: Declare dependencies
description: Declare the Python packages that your managed workflow needs.
source: https://quantum.cloud.ibm.com/docs/en/managed-workflows/managed-workflow-dependencies
---

# Declare dependencies

A managed workflow runs in a Python environment that it prepares in advance. The job cannot reach the internet, so it cannot `pip install` packages. Instead, Qiskit and a list of [supported packages](#check-which-packages-are-installed) are already installed, and you can [upload wheels](#add-your-own-packages) for any other package that your workflow needs.

## Check which packages are installed

Every job runs with Python 3.12 on Linux (x86\_64), with the following packages installed:

- `qiskit`, `qiskit-ibm-runtime`, `qiskit-serverless`, and their dependencies, such as `numpy`.
- The supported packages, such as `qiskit-aer`, `qiskit-addon-sqd`, `qiskit-addon-utils`, `qiskit-mitigation`, `qiskit-addon-slc`, `qiskit-paulice`, `ffsim`, and `pyscf`. Each is installed at one fixed version.

To see the supported packages and their versions, call `dependencies_versions()` from your local notebook:

```python
from qiskit_ibm_catalog import ManagedWorkflowClient

client = ManagedWorkflowClient()
client.dependencies_versions()
```

Your workflow can import any of these packages without setup. The workflow installs them when it builds the environment, not when a job starts, so they do not add to the start time of a job.

A workflow does not read a `requirements.txt` or `pyproject.toml` file, so it does not install the packages that these files list. The `dependencies` argument of `ManagedWorkflow` is deprecated and ignored, so it does not install packages either. Remove it from your upload code, and [upload wheels](#add-your-own-packages) instead.

### Packages on GPU compute profiles

Jobs that run on a [GPU compute profile](/docs/managed-workflows/managed-workflow-compute-profiles) also have the following CUDA packages installed. These packages work only on a GPU profile, and `dependencies_versions()` does not list them. Do not upload your own CUDA wheels, because they conflict with these versions.

| Package                    | Version    |
| -------------------------- | ---------- |
| `gpu4pyscf-cuda12x`        | 1.8.1      |
| `gpu4pyscf-libxc-cuda12x`  | 0.8.1      |
| `cupy-cuda12x`             | 14.2.0     |
| `cutensor-cu12`            | 2.8.0      |
| `cuda-pathfinder`          | 1.8.5      |
| `nvidia-cublas-cu12`       | 12.9.3.13  |
| `nvidia-cuda-nvrtc-cu12`   | 12.9.86    |
| `nvidia-cuda-runtime-cu12` | 12.9.79    |
| `nvidia-cufft-cu12`        | 11.4.1.4   |
| `nvidia-curand-cu12`       | 10.3.10.19 |
| `nvidia-cusolver-cu12`     | 11.7.5.82  |
| `nvidia-cusparse-cu12`     | 12.5.10.65 |
| `nvidia-nvjitlink-cu12`    | 12.9.86    |

The `nvidia-*-cu12` versions can change when the environment is rebuilt. Code that depends on an exact CUDA library version might need updates after a new release.

### Request a package

If other users would also need a package, [request it on GitHub](https://github.com/Qiskit/qiskit-serverless/issues/new?template=pip_dependency_request.yaml). An accepted package is added to the environment in a later release. Until then, or for a private package, [add it as a wheel](#add-your-own-packages).

## Add your own packages

To use a package that is not installed, collect its wheel files on your computer, upload them to the workflow's file storage, and install them at the start of the workflow. You upload them again only when they change.

### Build wheels

The job installs wheels without an internet connection, so each wheel must work with Python 3.12 on Linux (x86\_64). Collect the wheels in one folder:

```bash
# Your own package
pip wheel --no-deps --wheel-dir wheels ./mypkg

# A package from PyPI, for the job's platform
pip download --only-binary=:all: --platform manylinux_2_28_x86_64 \
    --python-version 3.12 --dest wheels pendulum==3.1.0
```

With `--platform` and `--python-version`, `pip download` gets wheels for the job's platform, even when you run it on macOS or Windows. It also downloads the package's dependencies, such as `python-dateutil`, `six`, and `tzdata` for `pendulum`. Because the job installs each wheel without its dependencies, upload a wheel for every dependency that the environment lacks, and delete the other wheels from the folder.

A pure-Python wheel (`py3-none-any.whl`) works on any platform. If a package with compiled code has no Linux x86\_64 wheel on PyPI, build one on Linux, for example in a `python:3.12` container. Keep the original file names, because `pip` reads the package name, version, and platform from them.

### Upload wheels

Upload the wheels from your local notebook with `add_wheels()`:

```python
from qiskit_ibm_catalog import ManagedWorkflowClient

client = ManagedWorkflowClient()
my_workflow = client.load("my-workflow")

client.add_wheels(my_workflow, ["wheels/*.whl"])
print(client.list_wheels(my_workflow))
```

Each path can be a file name or a glob pattern, and `add_wheels()` returns the names of the uploaded wheels. If a pattern matches no files, or matches a file that is not a `.whl` file, `add_wheels()` raises a `QiskitServerlessException`.

Upload the workflow before you add wheels, because the wheels are stored with it. They go to a `wheels` folder in the workflow's [file storage](/docs/managed-workflows/manage-files-across-managed-workflows), separate from your other files.

#### Replace or remove wheels

To see the wheels that are uploaded to a workflow, call `list_wheels()`. To remove wheels, pass their file names to `remove_wheels()`:

```python
client.list_wheels(my_workflow)
# ['mypkg-1.0-py3-none-any.whl']

client.remove_wheels(my_workflow, ["mypkg-1.0-py3-none-any.whl"])
```

To upgrade a package, remove the old wheel, and then add the new one. A new version has a different file name, so if you keep the old wheel, `install_uploaded_wheels()` installs both versions.

```python
client.remove_wheels(my_workflow, ["mypkg-1.0-py3-none-any.whl"])
client.add_wheels(my_workflow, ["wheels/mypkg-1.1-*.whl"])
```

### Install wheels in your workflow

Edit your workflow code to call `install_uploaded_wheels()` before it imports the packages:

```python
from qiskit_serverless import install_uploaded_wheels

install_uploaded_wheels()

import pendulum  # Installed from an uploaded wheel
```

`install_uploaded_wheels()` installs every wheel in the `wheels` folder. If the workflow has no wheels, the call does nothing. The wheels are installed again in every run, so a large set of wheels adds to the start time of each job.

## Best practice: bring only missing packages

As a best practice, upload wheels only for the packages that the environment lacks. A wheel for an installed package, such as `numpy`, installs a second copy, and the code that imports it can get either copy. Errors from a mismatch appear only when the job runs.

Before you upload, check your requirements with `dependency_report()` from your local notebook:

```python
print(client.dependency_report(["pendulum==3.1.0", "qiskit-aer", "qiskit-addon-sqd==0.10.0"]))
```

```text
requirement               status  note
pendulum==3.1.0           bring   not in image — upload a wheel
qiskit-aer                baked   already in image (qiskit-aer==0.17.2)
qiskit-addon-sqd==0.10.0  clash   image ships qiskit-addon-sqd==0.13.1
```

The report gives each requirement one of the following statuses:

- `baked`: The environment already provides the package. Do not upload a wheel for it.
- `bring`: The environment lacks the package. Upload a wheel for it.
- `clash`: The environment provides a different version. Change the version that your code requires, or rebuild your package for the installed version.

The report checks only the supported packages, so it shows `qiskit`, `qiskit-ibm-runtime`, `qiskit-serverless`, and `numpy` as `bring`. The environment includes them, so do not upload wheels for them. The report shows `clash` only for an exact `==` version. It shows a range, such as `>=0.10`, as `baked`. The report does not upload any files.

## Troubleshoot dependencies

| Problem                                                                                 | Cause                                                                                                                                                                                |
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `pip install` fails inside the workflow.                                                | The job cannot reach the internet. [Upload a wheel](#add-your-own-packages) instead.                                                                                                 |
| `ModuleNotFoundError` for a package that you uploaded.                                  | The workflow imports the package before it calls `install_uploaded_wheels()`.                                                                                                        |
| `ModuleNotFoundError` for a dependency of a package that you uploaded.                  | Wheels are installed without their dependencies. Upload a wheel for the missing dependency.                                                                                          |
| The error says that a wheel `is not a supported wheel on this platform`.                | The wheel is for another operating system or Python version. Get a wheel for Python 3.12 on Linux x86\_64.                                                                           |
| `install_uploaded_wheels()` fails with `ResolutionImpossible`.                          | The `wheels` folder has two versions of the same package. Remove the old wheel with [`remove_wheels()`](#replace-or-remove-wheels).                                                  |
| A CUDA package, such as `cupy`, fails on a GPU compute profile after you upload wheels. | A wheel replaced one of the [CUDA packages](#packages-on-gpu-compute-profiles) that the environment provides. Remove that wheel with [`remove_wheels()`](#replace-or-remove-wheels). |
| An installed package, such as `numpy`, fails after you upload wheels.                   | A wheel installed a second copy of a package that the environment provides. Remove that wheel with [`remove_wheels()`](#replace-or-remove-wheels).                                   |
