error: failed to prepare transaction (could not satisfy dependencies)
:: unable to satisfy dependency 'libavcodec.so=62-64' required by punktfunk-host
:: unable to satisfy dependency 'libavutil.so=60-64' required by punktfunk-host
:: unable to satisfy dependency 'libavfilter.so=11-64' required by punktfunk-host
:: unable to satisfy dependency 'libavdevice.so=62-64' required by punktfunk-host
:: unable to satisfy dependency 'libswscale.so=9-64' required by punktfunk-host
The published punktfunk-host 0.25.0-1 declares FFmpeg 8 sonames; current Arch ships
63/61/12/63/10. Confirmed straight from the registry DB, and reproduced verbatim on a real
ffmpeg-9 box.
It fails safely — pacman refuses, nothing bricks, which is exactly what PR #108 was for — but
it fails broadly: pacman prepares one transaction, so an unsatisfiable dependency of ours
stops the user's entirepacman -Syu, not just our package. Hence the field report reading
as "the package requires 8". punktfunk-canary has the same defect.
canary / stable built — still in the FFmpeg-8 :latest
later
the re-keyed FFmpeg-9 builder image lands
arch.yml and docker.yml have no ordering between them, and arch.yml deliberately runs no -Syu ("the image's snapshot IS the build environment"). Only Arch was ever exposed: every
other format derives its dependency from the ELF at build time, so it cannot disagree with
itself this way.
Nothing in the pipeline could have caught it. The existing assert proves the libav dep is versioned; it cannot prove the version exists.
The change
Preflight parity — compare the builder's libav provides against the live repos, -Syu
the container if they differ. The image is a cache and may lag; on this one axis it may not.
Syncs into a throwaway --dbpath, so the container never sits in the partial-upgrade state a
bare pacman -Sy leaves. Also makes it moot whether :latest is current on any given day.
Publish gate — resolve every built package with pacman -U --print against a pristine --dbpath. An empty db means "nothing is installed", so every dependency must come from the
repos exactly as on a user's box. Resolving against the builder's own installed set is
precisely what hides this: a stale ffmpeg satisfies a stale bound. gamescope stays
best-effort (dropped from the upload with a warning, never fatal).
workflow_dispatch(release_tag, pkgrel) — a published release cannot be repaired by
re-running its tag: pkgrel would stay 1, which is invisible to a box that already recorded
the broken build, and the workflow file at a tag can never carry inputs added after it.
Dispatched from main it takes the workflow from main and the source from the tag,
publishes to the stable repo at a higher pkgrel, and replaces the release-page assets
(prune_release_assets: upsert_asset replaces by NAME, and a rebuild's filenames differ, so
the superseded package would otherwise stay one click away — scoped to .pkg.tar.zst so
concurrent packaging legs can only ever prune names they own).
Docs: the pacman error and what to do about it in the Arch README (plus the duplicate [punktfunk] block note — visible in the same field screenshot); CHANGELOG says plainly that
0.25.0's Arch packages were wrong and that 0.25.0-2 is the fix.
Verification — on a real ffmpeg-9 Arch box, not reasoned about
The gate against all five published v0.25.0 artifacts: hostFAIL (user-visible error
verbatim), client / web / scripting / gamescopeOK. Zero false positives — it would
have blocked this release.
Parity snippet verbatim: -Si --dbpath on an empty db works, pacman does not wrap fields when
piped, today's provides read correctly.
Version logic on all four paths: rebuild → 0.25.0-2 stable, tag push → 0.25.0-1 stable,
canary unchanged, pkgrel=1 refused.
shellcheck clean on gitea-release.sh; asset-prune selector unit-tested (keeps the new set,
drops the superseded set, leaves other legs' .dmg/.deb untouched).
Landing it
Merging this alone heals the canary channel: arch.yml's path filter includes itself, so the
merge triggers a canary build that exercises both guards. The stable repair is then a
dispatch of arch.yml with release_tag=v0.25.0, pkgrel=2, publishing punktfunk-host 0.25.0-2
— identical source, linked against FFmpeg 9.
## What users see
```
error: failed to prepare transaction (could not satisfy dependencies)
:: unable to satisfy dependency 'libavcodec.so=62-64' required by punktfunk-host
:: unable to satisfy dependency 'libavutil.so=60-64' required by punktfunk-host
:: unable to satisfy dependency 'libavfilter.so=11-64' required by punktfunk-host
:: unable to satisfy dependency 'libavdevice.so=62-64' required by punktfunk-host
:: unable to satisfy dependency 'libswscale.so=9-64' required by punktfunk-host
```
The published `punktfunk-host 0.25.0-1` declares **FFmpeg 8** sonames; current Arch ships
63/61/12/63/10. Confirmed straight from the registry DB, and reproduced verbatim on a real
ffmpeg-9 box.
It fails *safely* — pacman refuses, nothing bricks, which is exactly what PR #108 was for — but
it fails *broadly*: pacman prepares one transaction, so an unsatisfiable dependency of **ours**
stops the user's **entire** `pacman -Syu`, not just our package. Hence the field report reading
as "the package requires 8". `punktfunk-canary` has the same defect.
## Why
A four-minute race, not a design error.
| time (2026-08-08) | |
|---|---|
| 00:41 | PR #108 merges: soname depends + FFmpeg 9 + `ci/arch-ci.Dockerfile` re-keyed |
| 00:45 | `v0.25.0` tagged → arch.yml starts |
| 00:56 / 01:02 | canary / **stable** built — still in the FFmpeg-8 `:latest` |
| later | the re-keyed FFmpeg-9 builder image lands |
`arch.yml` and `docker.yml` have no ordering between them, and arch.yml deliberately runs no
`-Syu` ("the image's snapshot IS the build environment"). Only Arch was ever exposed: every
other format derives its dependency from the ELF at build time, so it cannot disagree with
itself this way.
**Nothing in the pipeline could have caught it.** The existing assert proves the libav dep is
*versioned*; it cannot prove the version *exists*.
## The change
- **Preflight parity** — compare the builder's libav `provides` against the live repos, `-Syu`
the container if they differ. The image is a cache and may lag; on this one axis it may not.
Syncs into a throwaway `--dbpath`, so the container never sits in the partial-upgrade state a
bare `pacman -Sy` leaves. Also makes it moot whether `:latest` is current on any given day.
- **Publish gate** — resolve every built package with `pacman -U --print` against a **pristine**
`--dbpath`. An empty db means "nothing is installed", so every dependency must come from the
repos exactly as on a user's box. Resolving against the builder's own installed set is
precisely what hides this: a stale ffmpeg satisfies a stale bound. gamescope stays
best-effort (dropped from the upload with a warning, never fatal).
- **`workflow_dispatch(release_tag, pkgrel)`** — a published release cannot be repaired by
re-running its tag: pkgrel would stay `1`, which is invisible to a box that already recorded
the broken build, and the workflow file at a tag can never carry inputs added after it.
Dispatched from `main` it takes the *workflow* from main and the *source* from the tag,
publishes to the stable repo at a higher pkgrel, and replaces the release-page assets
(`prune_release_assets`: `upsert_asset` replaces by NAME, and a rebuild's filenames differ, so
the superseded package would otherwise stay one click away — scoped to `.pkg.tar.zst` so
concurrent packaging legs can only ever prune names they own).
- Docs: the pacman error and what to do about it in the Arch README (plus the duplicate
`[punktfunk]` block note — visible in the same field screenshot); CHANGELOG says plainly that
0.25.0's Arch packages were wrong and that `0.25.0-2` is the fix.
## Verification — on a real ffmpeg-9 Arch box, not reasoned about
- The gate against all five **published** v0.25.0 artifacts: `host` **FAIL** (user-visible error
verbatim), `client` / `web` / `scripting` / `gamescope` **OK**. Zero false positives — it would
have blocked this release.
- Parity snippet verbatim: `-Si --dbpath` on an empty db works, pacman does not wrap fields when
piped, today's `provides` read correctly.
- Version logic on all four paths: rebuild → `0.25.0-2` stable, tag push → `0.25.0-1` stable,
canary unchanged, `pkgrel=1` refused.
- `shellcheck` clean on `gitea-release.sh`; asset-prune selector unit-tested (keeps the new set,
drops the superseded set, leaves other legs' `.dmg`/`.deb` untouched).
## Landing it
Merging this alone heals the **canary** channel: arch.yml's path filter includes itself, so the
merge triggers a canary build that exercises both guards. The **stable** repair is then a
dispatch of arch.yml with `release_tag=v0.25.0`, `pkgrel=2`, publishing `punktfunk-host 0.25.0-2`
— identical source, linked against FFmpeg 9.
Arch moved FFmpeg 8 -> 9 (every libav soname +1) hours before the release. PR #108
fixed the real bug — packaging/arch/PKGBUILD now binds punktfunk-host to the sonames
it actually linked, so pacman refuses an upgrade instead of bricking the install — and
re-keyed ci/arch-ci.Dockerfile so the builder would carry FFmpeg 9.
The tag was pushed four minutes later. arch.yml and docker.yml have no `needs:` between
them, and arch.yml deliberately runs no -Syu ("the image's snapshot IS the build
environment"), so the release build pulled the still-FFmpeg-8 `:latest` and published
punktfunk-host 0.25.0-1 depends: libavcodec.so=62-64, libavutil.so=60-64,
libavfilter.so=11-64, libavdevice.so=62-64,
libswscale.so=9-64
against a world that had moved to 63/61/12/63/10. It fails safely — pacman refuses,
nothing bricks — but it fails broadly: pacman prepares one transaction, so an
unsatisfiable dependency of OURS stopped affected users' entire `pacman -Syu`.
Nothing in the pipeline could have caught it. The existing assert proves the dep is
VERSIONED; it cannot prove the version EXISTS. So two guards, plus the lever to repair
a release that has already shipped:
* Preflight parity — compare the builder's libav `provides` against the live repos and
`-Syu` the container if they differ. The image is a cache and may lag; on this one
axis it may not. Syncs into a throwaway --dbpath so the container never sits in the
partial-upgrade state a bare `pacman -Sy` leaves.
* Publish gate — resolve every built package with `pacman -U --print` against a
PRISTINE --dbpath. Empty db means "nothing is installed", so every dependency must
come from the repos exactly as on a user's box. Resolving against the builder's own
installed set is what would hide this: a stale ffmpeg satisfies a stale bound.
gamescope stays best-effort (dropped from the upload with a warning, never fatal).
* workflow_dispatch(release_tag, pkgrel) — a published release cannot be repaired by
re-running its tag: pkgrel would stay 1, which is invisible to a box that already
recorded the broken build, and the workflow file at the tag can never carry inputs
added after it. Dispatched from main it takes the WORKFLOW from main and the SOURCE
from the tag, publishes to the stable repo at a higher pkgrel, and replaces the
release-page assets (prune_release_assets: upsert replaces by NAME, and a rebuild's
filenames differ, so the superseded package would otherwise stay one click away).
Verified on a real ffmpeg-9 box (.21, CachyOS) rather than reasoned about: the gate
rejects the published 0.25.0-1 host with the user-visible error verbatim, and passes
client, web, scripting and gamescope — 0 false positives across all five artifacts.
The parity snippet reads today's `provides` correctly (`-Si --dbpath` on an empty db
works; pacman does not wrap fields when piped). Version logic exercised on all four
paths: rebuild -> 0.25.0-2 stable, tag push and canary unchanged, pkgrel=1 refused.
Ships as punktfunk-host 0.25.0-2. README gains the pacman error and what to do about
it; CHANGELOG says plainly that 0.25.0's Arch packages were wrong.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What users see
The published
punktfunk-host 0.25.0-1declares FFmpeg 8 sonames; current Arch ships63/61/12/63/10. Confirmed straight from the registry DB, and reproduced verbatim on a real
ffmpeg-9 box.
It fails safely — pacman refuses, nothing bricks, which is exactly what PR #108 was for — but
it fails broadly: pacman prepares one transaction, so an unsatisfiable dependency of ours
stops the user's entire
pacman -Syu, not just our package. Hence the field report readingas "the package requires 8".
punktfunk-canaryhas the same defect.Why
A four-minute race, not a design error.
ci/arch-ci.Dockerfilere-keyedv0.25.0tagged → arch.yml starts:latestarch.ymlanddocker.ymlhave no ordering between them, and arch.yml deliberately runs no-Syu("the image's snapshot IS the build environment"). Only Arch was ever exposed: everyother format derives its dependency from the ELF at build time, so it cannot disagree with
itself this way.
Nothing in the pipeline could have caught it. The existing assert proves the libav dep is
versioned; it cannot prove the version exists.
The change
providesagainst the live repos,-Syuthe container if they differ. The image is a cache and may lag; on this one axis it may not.
Syncs into a throwaway
--dbpath, so the container never sits in the partial-upgrade state abare
pacman -Syleaves. Also makes it moot whether:latestis current on any given day.pacman -U --printagainst a pristine--dbpath. An empty db means "nothing is installed", so every dependency must come from therepos exactly as on a user's box. Resolving against the builder's own installed set is
precisely what hides this: a stale ffmpeg satisfies a stale bound. gamescope stays
best-effort (dropped from the upload with a warning, never fatal).
workflow_dispatch(release_tag, pkgrel)— a published release cannot be repaired byre-running its tag: pkgrel would stay
1, which is invisible to a box that already recordedthe broken build, and the workflow file at a tag can never carry inputs added after it.
Dispatched from
mainit takes the workflow from main and the source from the tag,publishes to the stable repo at a higher pkgrel, and replaces the release-page assets
(
prune_release_assets:upsert_assetreplaces by NAME, and a rebuild's filenames differ, sothe superseded package would otherwise stay one click away — scoped to
.pkg.tar.zstsoconcurrent packaging legs can only ever prune names they own).
[punktfunk]block note — visible in the same field screenshot); CHANGELOG says plainly that0.25.0's Arch packages were wrong and that
0.25.0-2is the fix.Verification — on a real ffmpeg-9 Arch box, not reasoned about
hostFAIL (user-visible errorverbatim),
client/web/scripting/gamescopeOK. Zero false positives — it wouldhave blocked this release.
-Si --dbpathon an empty db works, pacman does not wrap fields whenpiped, today's
providesread correctly.0.25.0-2stable, tag push →0.25.0-1stable,canary unchanged,
pkgrel=1refused.shellcheckclean ongitea-release.sh; asset-prune selector unit-tested (keeps the new set,drops the superseded set, leaves other legs'
.dmg/.debuntouched).Landing it
Merging this alone heals the canary channel: arch.yml's path filter includes itself, so the
merge triggers a canary build that exercises both guards. The stable repair is then a
dispatch of arch.yml with
release_tag=v0.25.0,pkgrel=2, publishingpunktfunk-host 0.25.0-2— identical source, linked against FFmpeg 9.