"libxml2.so.2" or "libicudata.so.74": cannot open shared object file on ubuntu-latest (Ubuntu 26.04)
ubuntu-latest is moving from Ubuntu 24.04 to 26.04 between October 19 and November 19, 2026 (what else changes). Two libraries that many programs link to have new names on 26.04. libxml2 is version 2.15 in the package libxml2-16, so the file is libxml2.so.16; 24.04 had libxml2.so.2 in the package libxml2. ICU is version 78 (libicu78); 24.04 had ICU 74, with libicuuc.so.74, libicudata.so.74 and libicui18n.so.74. A program built on 24.04 asks for the old names and stops before it does anything.
The messages
WebKit from a Playwright browser cache saved on 24.04 (owl-be-there #46, 260 WebKit tests failed at launch). Chromium and Firefox bring their own libraries and still pass (tessera #364):
MiniBrowser: error while loading shared libraries: libicudata.so.74: cannot open shared object file
An R package library restored from a 24.04 cache (flu-scenario-modeling-hub #55):
unable to load shared object '.../Library/stringi/libs/stringi.so': libicui18n.so.74: cannot open shared object file
A Ruby gem bundle restored from a 24.04 cache, with the libxml-ruby extension in it (bulkrax #1266, every spec file failed to load):
LoadError: libxml2.so.2: cannot open shared object file
A prebuilt binary made for 24.04, here SwiftLint 0.65.1 from its release archive, exit code 127 (SwiftLint #6966):
swiftlint: error while loading shared libraries: libxml2.so.2: cannot open shared object file: No such file or directory
A cache saved on 24.04
runner.os is Linux on every Ubuntu image. A key such as ${{ runner.os }}-${{ hashFiles('Gemfile.lock') }} is the same on 24.04 and 26.04, so the 26.04 job restores what a 24.04 job built, and anything compiled in it links the old libraries. Put the image in the key. Hosted runners set ImageOS to ubuntu24 on 24.04 and ubuntu26 on 26.04:
- id: image
run: echo "os=${ImageOS:?ImageOS is not set on this runner}" >> "$GITHUB_OUTPUT"
- uses: actions/cache@v6
with:
path: vendor/bundle
key: gems-${{ runner.os }}-${{ steps.image.outputs.os }}-${{ hashFiles('Gemfile.lock') }}
restore-keys: gems-${{ runner.os }}-${{ steps.image.outputs.os }}-
Read it in a step, as above. ${{ env.ImageOS }} is empty: the env context only holds variables the workflow sets, not the runner's own (GitHub's contexts reference). dorkos #3347 found its keys reading pnpm-store-Linux--X64-... on both images after such a fix. ${ImageOS:?...} makes the step fail where the variable is unset, for example on a self-hosted runner, instead of keying on an empty string.
Change restore-keys too: a prefix without the image still finds the 24.04 entry. The first run on each image misses the cache once and saves a new one. For Playwright, keep the Playwright version in the key as well; it downloads separate browser builds for 26.04.
The caches of actions/setup-python, actions/setup-go and the bundler-cache of ruby/setup-ruby already put the Ubuntu version in their keys. The keys at risk are the ones you wrote for actions/cache.
A binary or wheel built for 24.04
Here the cache is not involved, and a fresh download gives the same error. Look for a build made for 26.04, or a statically linked one. Reports so far include SwiftLint's dynamically linked Linux binary (#6966, open on November 4) and the PyPI wheel of libcellml, for which circulatory_autogen #622 moved its CI back to 24.04. apt-get install libxml2 does not help on 26.04: that package does not exist there, and libxml2-16 has only libxml2.so.16. Until the project ships a build for 26.04, keep that job on runs-on: ubuntu-24.04.
Telling it apart
- The program stops at start with "error while loading shared libraries", often with exit code 127, before printing anything of its own.
ldd path/to/program | grep 'not found'lists every missing library.- In the log of the cache step, "Cache restored from key: ..." shows the key. If it has no image in it, the entry may come from 24.04. Deleting that entry, or changing the key, and running again tells you whether the cache is the cause.
- Look at "Set up job": the Runner Image group says
ubuntu-26.04orubuntu-24.04. Until November 19 a re-run can land on 24.04 and pass.
Other 26.04 failures
- C and C++ builds that fail with GCC 15 on Ubuntu 26.04: the default gcc goes from 13 to 15, so C is compiled as C23, six old warnings are errors, and C++ headers no longer pull in
<cstdint>for you. - setup-ruby's "Unavailable version" on Ubuntu 26.04: only some patch releases of each Ruby series are built for 26.04, so a pinned 3.4.2 in
.ruby-versionstops at the Ruby step. - Playwright on Ubuntu 26.04: Playwright 1.50 to 1.60 says it "does not support chromium on ubuntu26.04-x64".
- apt on Ubuntu 26.04: renamed and removed packages.
- rootless Podman on Ubuntu 26.04: the AppArmor profile of
pastarefuses Podman's SIGTERM, sodocker compose downover the Podman socket fails with "rootless netns: kill network process: permission denied". - "client version ... is too old" on Ubuntu 26.04: Docker 29 refuses clients below API 1.40, so older Testcontainers fails with "Could not find a valid Docker environment".
- "coreutils: unknown program": most core tools are uutils.
See failed jobs from IntelliJ IDEA
Pipeline Inbox for GitHub Actions Workflows, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, lists the runs of all your repositories in an IDE tool window, with their jobs and steps, and your current branch's runs are in the status bar. Reading is free; re-running failed jobs, cancelling, running workflows and reviewing deployments are Pro (US$1.50 a month, with a 30-day trial).
Get it on JetBrains Marketplace
GitHub and GitHub Actions are trademarks of GitHub, Inc.; Ubuntu is a trademark of Canonical Ltd.; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with, sponsored or endorsed by GitHub, Inc., Canonical or JetBrains.
More: "The ubuntu-latest label will migrate to Ubuntu 26" · "Cache not found for input keys" · Playwright on Ubuntu 26.04 · GitHub Actions plugins for IntelliJ IDEA compared · 日本語