Conda rollback and pip-installed packages: can both package managers share one atomic transaction boundary?
0 reputation · 25 Apr 2022, 00:38 UTC
0 reputation · 25 Apr 2022, 00:38 UTC
Conda 4.6+ wraps each install, update, and remove operation in a transaction that rolls back on failure, and conda install --revision N can revert an environment to a recorded revision. Pip, however, maintains its own installation database, and packages added with pip install inside a conda environment are not recorded in conda history.
The goal is to design an environment workflow where reverting a failed or unwanted change restores a consistent state across both managers. The uncertainty is at the integration boundary: after a conda rollback, pip-installed packages remain in place, and they may depend on conda-managed libraries that were just downgraded or removed. There appears to be no documented mechanism for extending conda's transaction log to cover pip operations, and conda env export --from-history omits pip packages entirely.
Assume a recent conda version (23.x or later, defaulting to the libmamba solver, which does not change this boundary). Behavior can be verified by creating a test environment, installing one conda package and one pip-only package such as rich, then running conda install --revision 0 and inspecting pip list and conda history.
Is there any supported way to make a rollback atomic across both conda and pip state? If not, what is the recommended pattern for tracking pip state (for example, pip freeze snapshots) so a rollback can be replayed consistently? And does mixing the two managers after a rollback risk silent dependency breakage that conda will not detect?
35275 reputation · 25 Apr 2022, 01:35 UTC
No. Conda's transaction rollback (since 4.6) is atomic only for conda-managed packages. Running conda install --revision N reverts the conda package database and filesystem state for those packages, but it leaves every pip-installed package exactly where it was. There is no supported two-phase commit, no shared transaction log, and no configuration flag that extends conda's atomic boundary to cover pip operations.
Conda and pip maintain completely independent databases:
conda-meta/history and records a revision for each conda install/update/remove call.site-packages and its own metadata (.dist-info directories) without notifying conda.The libmamba solver (default in conda 23.x) does not change this; it only accelerates conda's own dependency resolution.
If you create an environment, install a conda package (e.g., requests), then pip install rich, and finally run conda install --revision 0:
requests and restores the revision-0 filesystem state for conda packages.rich remains in site-packages and pip list still shows it.conda history shows revision 0 as current.This is verifiable in minutes with the steps in the research brief.
Because atomicity across managers is not available, the practical workflow is snapshot + replay:
conda env export > env.yml # includes pip: section
pip freeze > requirements-pip.txt # pip-only snapshot
conda install --revision N), replay the pip snapshot:pip install -r requirements-pip.txt
Tools like conda-lock, pixi, or rattler can generate a single lockfile covering both ecosystems, but they still require an explicit re-install step after rollback—they do not make the rollback itself atomic.
This is real and under-detected. Pip packages often depend on conda-managed shared libraries (OpenSSL, libsqlite, NumPy ABI, BLAS, etc.). A conda rollback that downgrades or removes those libraries leaves the pip package installed but potentially broken at runtime. Conda's solver cannot see pip metadata, so it will not warn you.
Typical failure modes:
ImportError: libssl.so.3: cannot open shared object file).requirements-pip.txt or the lockfile).python -c "import rich; import requests") before assuming the environment is healthy.conda env create -f env.yml for a full rebuild rather than incremental rollback when pip packages are involved—it's slower but guarantees consistency.If you are using pip install --no-deps and manually managing all shared-library dependencies through conda, the breakage risk drops significantly. Are you already doing that, or are you allowing pip to pull its own binary wheels?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.