Summary
cachedInit() keys the virtualenv cache on trellis-venv + hashFiles('**/requirements.txt'):
async function cachedInit() {
return await withCache(async () => {
await exec.exec('trellis init')
}, [path.join('.trellis', 'virtualenv')], 'trellis-venv', '**/requirements.txt');
}
The key does not include the Python version. But a virtualenv's bin/python3 is an absolute symlink into /opt/hostedtoolcache/Python/<patch>/x64. So when requirements.txt is unchanged and the runner's Python patch rolls, the cache still hits and restores a venv pointing at an interpreter that no longer exists:
⠋ Creating virtualenv
Error: [Errno 2] No such file or directory: '.../trellis/.trellis/virtualenv/bin/python3'
[✘] Error creating virtualenv
Project initialization failed due to the error above.
Why it's easy to misdiagnose
The failure is intermittent and appears to correlate with whatever PR merged that morning, because the cache — not the diff — determines the outcome. In our repo it was twice attributed to unrelated application-code PRs before we traced it.
Observed sequence, same workflow, python-version: "3.12" (floating):
| Venv cache |
Result |
| hit |
fail |
| miss |
pass |
| miss (saves new cache) |
pass |
| hit |
fail |
| hit |
fail |
| miss (cache manually deleted) |
pass |
Deleting the cache entry is a reliable workaround, which isolates it to the cache rather than the project.
Suggested fix
Include the resolved Python version in the cache key, e.g. append process.env.pythonLocation or the output of python3 --version to the key alongside the requirements.txt hash. That makes the key describe what the artifact is actually bound to, and a Python roll becomes a clean cache miss and rebuild instead of a hard failure.
Workaround for anyone hitting this
Either pin the exact patch:
- uses: actions/setup-python@v5
with:
python-version: "3.12.14" # not "3.12"
or set cache-virtualenv: false. Pinning keeps the cache and turns a future roll into an explicit "version not found" at setup time rather than the venv error above.
Environment
roots/setup-trellis-cli@v1, trellis-cli v1.19.0
ubuntu-latest, actions/setup-python@v5, Python 3.12.14
Summary
cachedInit()keys the virtualenv cache ontrellis-venv+hashFiles('**/requirements.txt'):The key does not include the Python version. But a virtualenv's
bin/python3is an absolute symlink into/opt/hostedtoolcache/Python/<patch>/x64. So whenrequirements.txtis unchanged and the runner's Python patch rolls, the cache still hits and restores a venv pointing at an interpreter that no longer exists:Why it's easy to misdiagnose
The failure is intermittent and appears to correlate with whatever PR merged that morning, because the cache — not the diff — determines the outcome. In our repo it was twice attributed to unrelated application-code PRs before we traced it.
Observed sequence, same workflow,
python-version: "3.12"(floating):Deleting the cache entry is a reliable workaround, which isolates it to the cache rather than the project.
Suggested fix
Include the resolved Python version in the cache key, e.g. append
process.env.pythonLocationor the output ofpython3 --versionto the key alongside therequirements.txthash. That makes the key describe what the artifact is actually bound to, and a Python roll becomes a clean cache miss and rebuild instead of a hard failure.Workaround for anyone hitting this
Either pin the exact patch:
or set
cache-virtualenv: false. Pinning keeps the cache and turns a future roll into an explicit "version not found" at setup time rather than the venv error above.Environment
roots/setup-trellis-cli@v1, trellis-cli v1.19.0ubuntu-latest,actions/setup-python@v5, Python 3.12.14