[BUG] `setup-python` fails across multiple Windows and action versions when caching is requested and newer pip outputs a deprecation warning (any really)
UPD(17.02.2025): To anyone landing here from Google or hitting this bug otherwise
[!tip] Here's the solution I landed on, not only because of just this bug, but I've also been wanting better cache control for quite a while, so I've wrapped what I've been copying and pasting for years into a more reusable component — a composite action that can sense whether it's safe to cache things based on the current runtime stability (ABI stability is computed based on the version being marked as final). Feel free to make use of it: https://github.com/re-actors/cache-python-deps.
The sample use would be
- name: Restore pip cache uses: re-actors/cache-python-deps@release/v1 with: cache-key-for-dependency-files: >- ${{ hashFiles( '.pre-commit-config.yaml', 'requirements/**', 'tox.ini', 'tox.toml', 'pyproject.toml' ) }}provided that it's invoked after
setup-pythonand after a sort of checkout (or similar). Integration example: https://github.com/aio-libs/frozenlist/pull/622 / https://github.com/aio-libs/frozenlist/pull/633.P.S. The root cause of the bug is that
setup-pythonthreads any stderr frompipas a failure in any Windows runtimes. New pip started outputting a warning about a CLI flag being deprecated. That CLI flag is often being set via env vars likePIP_NO_PYTHON_VERSION_WARNING(meant to suppress another warning in the output). More explanation here: https://github.com/actions/setup-python/issues/1034#issuecomment-2661535880.
Description:
I've been updating a few GHA configurations today and in the process saw the same error in several places. Interestingly, in some repos it fails in one workflow but works in another. I've been seeing this for the past few hours, but this is in repos I haven't touched in months, so the issue might've appeared earlier.
I've tried finding differences and similarities, but cannot say anything definitive.
Among the similarities, there's these:
Current runner version: '2.322.0'
Runner Image
Image: windows-2019
Version: 20250209.1.0
Runner Image Provisioner
2.0.422.1
Also, the action fails on both windows-2019 and windows-2022. It fails in matrixes that pass a variety of Python versions, starting with 3.9 and all the way to 3.13.
Another bit I noticed was that some of the jobs downloaded immutable actions, while others fetched from Git. But there's examples of both in failing/passing action invocations.
Failing job examples (there's more in the same workflow runs):
- https://github.com/aio-libs/frozenlist/actions/runs/13314999958/job/37186948056#step:4:27
- https://github.com/cherrypy/cheroot/actions/runs/13314563336/job/37185295001#step:3:20
Success example:
- https://github.com/cherrypy/cheroot/actions/runs/13314453736/job/37185039139#step:2:27
As you can see, there's successes and failures in the same repo, just different job definitions.
While writing this, I also noticed that the failing ones have some sort of file preparation before calling setup-python and caching args enabled. Worth checking out... I'll report back once I experiment some more.
Action version:
v5
Platform:
- [ ] Ubuntu
- [ ] macOS
- [x] Windows
Runner type:
- [x] Hosted
- [ ] Self-hosted
Tools version:
🤷♂ seems to be any Python version and any windows image.
Repro steps:
Add a step with actions/setup-python@v5 into a windows job, set PIP_NO_PYTHON_VERSION_WARNING: 1 env var and add the cache: pip input.
Expected behavior:
It should succeed.
Actual behavior:
The action spits out Error: Could not get cache folder path for pip package manager, after the successful Installed versions output, and crashes the job.
So this experiment shows that dropping the cache inputs makes it pass: https://github.com/aio-libs/frozenlist/pull/622.
Hi @webknjaz ,Thank you for creating this issue. We will investigate it and provide feedback as soon as we have some updates.
This is the line that the action hits in the broken scenario: https://github.com/actions/setup-python/blob/8039c45ed9a312fba91f3399cd0605ba2ebfe93c/src/cache-distributions/pip-cache.ts#L46.
I tried various things so far.
Moving the requirements files mentioned in the cache-dependency-path to the top level of the repo does not help: https://github.com/aio-libs/frozenlist/actions/runs/13328642529/job/37227633874?pr=627#step:4:27. This excludes the possibility of this problem being related to non-normalized path separator (slash vs. backslash).
Moving setup-python before checkout makes it break due to the absence of the req files. This is expected.
Deleting the cache and cache-dependency-path inputs does work. So it's a viable workaround. https://github.com/aio-libs/frozenlist/actions/runs/13315768115/job/37189473317?pr=622#step:4:23
In an attempt to bisect the problem, I tried v5.2.0 (https://github.com/aio-libs/frozenlist/actions/runs/13328934530/job/37228641891?pr=629#step:4:27) and v5.1.0 (https://github.com/aio-libs/frozenlist/actions/runs/13328909086/job/37228544936?pr=628#step:4:27). Both are still broken.
Can the cache be corrupted somehow?
Older action versions are broken the same way: v5.0.0: https://github.com/aio-libs/frozenlist/actions/runs/13329181570/job/37229342141?pr=630#step:4:27 v4.0.0: https://github.com/aio-libs/frozenlist/actions/runs/13329182488/job/37229360342?pr=631#step:4:26
I wonder if something changed in the recently published CPython builds…
I wonder if something changed in the recently published CPython builds…
Nope... Requesting Python 3.13.0 published a while back (instead of 3.13.2) still results in an error: https://github.com/aio-libs/frozenlist/actions/runs/13329352448/job/37229847658#step:4:165
I thought that manual cache invalidation would help, but when I've gone to delete it in Cheroot, I realized that there's no cache prefixed with setup-python- in that repo. I delete the other manually-managed Windows cache, but it did not do anything.
I've checked frozenlist, and it does have caches prefixed with setup-python- but not with setup-python-Windows-. Can this be another bit of the puzzle? Does the reproducer require that there's no pre-existing cache saved?
Reading https://github.com/actions/setup-python/blob/8039c45ed9a312fba91f3399cd0605ba2ebfe93c/src/cache-distributions/pip-cache.ts#L24-L48, I have a theory of what's happening:
- There's
exitCode = 1at the beginning - But the windows branch does not set the return code, so it's always
1. - This means that
if (exitCode && stderr)is only influenced by stderr on Windows. - A new release of pip v25.0.1 got published on Feb 9.
- By default, pip prints out hints with "hey there's a new version available, wanna upgrade?" on any pip invocations.
- So when the action calls
pip cache dir, the bundled version of pip is older than the one on PyPI. - On Windows, that gets into
stderrbut the return code in the variable is not updated — it remains set to 1.
To summarize, this corner case is triggered on Windows but not *NIX, whenever a new version of pip is published to PyPI, but the respective Python build from https://github.com/actions/python-versions ships older pip. That older pip would print upgrade hints, which setup-python would misinterpret as error output on Windows.
This bug has been introduced by https://github.com/actions/setup-python/pull/332.
The only thing I was incorrect about was the warning being output, it seems. It's not the upgrade hint, but a deprecation:
DEPRECATION: --no-python-version-warning is deprecated. pip 25.1 will enforce this behaviour change. A possible replacement is to remove the flag as it's a no-op. Discussion can be found at https://github.com/pypa/pip/issues/13154
To anyone landing here from Google or hitting this bug otherwise
I've been wanting better cache control for quite a while, so I've wrapped what I've been doing for years into a more reusable component — a composite action that can sense whether it's safe to cache things based on the current runtime stability (ABI stability is computed based on the version being marked as final). Feel free to make use of it: https://github.com/re-actors/cache-python-deps.
The sample use would be
- name: Restore pip cache
uses: re-actors/cache-python-deps@release/v1
with:
cache-key-for-dependency-files: >-
${{
hashFiles(
'.pre-commit-config.yaml',
'requirements/**',
'tox.ini',
'tox.toml',
'pyproject.toml'
)
}}
provided that it's invoked after setup-python and after a sort of checkout (or similar).
Hello @webknjaz , thank you for the detailed report.
The issue arises from the --no-python-version-warning flag, which has become redundant and is no longer functional. This flag now triggers a warning to stderr from pip, leading to the failure. We are continuing our investigation into the matter.
As an immediate workaround, we recommend removing the PIP_NO_PYTHON_VERSION_WARNING environment variable. This will prevent the warning from being output to stderr, allowing your workflow to proceed without errors.
Alternatively, for Windows environments, you can use the actions/cache action after the setup-python action to manage caching. For more details, you can refer to the actions/cache documentation. Here is an example configuration:
- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~\AppData\Local\pip\Cache
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
@priyagupta108 yes, I know. I'm using it like that. Just made a wrapper action to abstract away a few other caching quirks better. setup-python's caching is not very sophisticated, anyway, and misses a few important points.
@webknjaz 👋,
The issue was originally caused by the --no-python-version-warning flag, which became redundant and triggered a warning from pip, leading to failures in workflows. However, the deprecation and removal of this flag has been reverted as per pypa/pip#13308. Going forward, the flag will be hidden from the CLI help and documentation, but it will no longer trigger errors or warnings.
Since the problem no longer persists, we are proceeding to close this issue. Please feel free to reach out if you have any concerns or need assistance.
Yeah, I know about the changes in pip. Special-casing stderr in select envs remains weird still. Anyway, my caching solution is better so I'll stick to it.