Nonceworks

setup-python: "The version '3.9' with architecture 'x64' was not found for Ubuntu 26.04"

Written 10 November 2026

Written 2026-11-10. If a GitHub Actions job that sets up Python 3.8 or 3.9 started failing at the actions/setup-python step, this is the cause. The short answer: there is no Python 3.8 or 3.9 build for Ubuntu 26.04, and ubuntu-latest is moving to 26.04. Pin that job to runs-on: ubuntu-24.04, drop the version from the matrix, or install it with uv.

The error

From a job log on the ubuntu-26.04 image (version 20261025.179.1), 2026-11-03:

Version ~3.9.0-0 was not found in the local cache
##[error]The version '3.9' with architecture 'x64' was not found for Ubuntu 26.04.
The list of all available versions can be found here: https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json

The same job fails with '3.8' in its other matrix leg. The step fails, so every later step and any job that needs: it is skipped. In the "Set up job" step, the "Runner Image" group shows which image the run got: Image: ubuntu-26.04 for this failure.

Why it happens now

setup-python does not compile Python. It downloads a prebuilt archive from the actions/python-versions manifest, matched to the runner's OS release. Builds for Ubuntu 26.04 started in June 2026 (python-versions#396, merged 2026-06-09), after Python 3.8 (2024-10-07) and 3.9 (2025-10-31) had reached end of life (Python release cycle). So no 3.8 or 3.9 build for 26.04 exists, and none is coming.

The manifest on 2026-11-10 (last changed 2026-11-04), x64 builds:

Python Ubuntu 24.04 Ubuntu 26.04
3.8 up to 3.8.18 none
3.9 up to 3.9.25 none
3.10 up to 3.10.22 3.10.20 to 3.10.22
3.11 up to 3.11.18 3.11.15 to 3.11.18
3.12 up to 3.12.16 3.12.13 to 3.12.16
3.13 up to 3.13.18 3.13.13 to 3.13.18
3.14 up to 3.14.10 3.14.6 to 3.14.10

The table catches a second case: an exact patch pin older than the first 26.04 build fails the same way. python-version: '3.10' works on 26.04 and gets 3.10.22, but '3.10.6' fails with The version '3.10.6' with architecture 'x64' was not found for Ubuntu 26.04. (3.10 itself reached end of life on 2026-10-01.)

GitHub is moving the x64 ubuntu-latest label to Ubuntu 26.04 between 2026-10-19 and 2026-11-19 (actions/runner-images#14748), so during those weeks the same workflow can pass on one run and fail on the next. More on that move in our Ubuntu 26.04 runner note.

The fixes

1. Run the old-Python jobs on Ubuntu 24.04. The 24.04 builds stay in the manifest. Pin only the legs that need 3.8 or 3.9, so the rest of the matrix moves on:

jobs:
  test:
    strategy:
      matrix:
        python: ["3.9", "3.10", "3.11", "3.12", "3.13"]
    runs-on: ${{ matrix.python == '3.9' && 'ubuntu-24.04' || 'ubuntu-latest' }}
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with:
          python-version: ${{ matrix.python }}

Public examples, merged: Free-TV/IPTV#1269 (one workflow pinned), kayak/pypika#3553 (the 3.8 and 3.9 legs pinned), ActivityWatch/activitywatch#1553 (four workflows). We have not seen a retirement date for the 24.04 image, but it is a stopgap.

2. Drop the version. Both are past end of life, so this is often the right fix: remove it from the matrix and raise requires-python (or python_requires, and any tox env) to match. A public example, merged: lululxvi/deepxde#2115. If a job only needed "some Python" for a tool such as a linter, move it to a supported version instead.

3. Install it with uv. uv downloads its own Python builds, which are not tied to the runner's OS release. In a container from the official ubuntu:26.04 image on 2026-11-10, uv 0.13.7 installed 3.8.20 and 3.9.25 with uv python install 3.8 and uv python install 3.9, and both ran. In a workflow, astral-sh/setup-uv sets the version and can put a virtual environment on the PATH:

      - uses: astral-sh/setup-uv@v10.3.3
        with:
          python-version: "3.9"
          activate-environment: true
      - run: uv pip install -e . && python -m pytest

We ran the uv commands, not this workflow on a hosted runner. A job that tests against end-of-life Python on purpose is the main reason to take this route.

Find it in your repos

grep -rnE "python-version:.*['\"]?3\.(8|9)\b|python: *\[.*3\.(8|9)" .github/workflows

Also look for exact patch pins such as 3.10.6 or 3.11.4 that are older than the first 26.04 build in the table, and for a .python-version file that setup-python reads through python-version-file.

If this break is in a public repository, we send the fix as a pull request for free; in a private repository it is $300, invoiced after you merge it. To catch the next one before it reaches a release, send your organisation's GitHub name to hello@nonceworks.com and we send back a free report on its public release, publish and nightly workflows (merged fixes, pricing).

Limits

  • The table is the x64 manifest on 2026-11-10. arm64 has the same 26.04 versions. Builds are added as Python releases come out.
  • Self-hosted runners on Ubuntu 26.04 get the same answer from the same manifest. Windows and macOS runners are not part of this move.

Sources