Closes #242.
Fixes the mechanism the issue's own follow-up comment identified, not the range matcher — satisfiesInstallerConstraint was never wrong.
The problem
fetchLiveVersions returned a bare string[], so [] from a rate-limited api.github.com (60 req/hr per IP, shared across a runner pool) was indistinguishable from [] from "upstream published nothing". resolveVersionConstraint unioned that empty list with the stale bundled catalog — ceiling 1.4.0 in the pinned action build — and reported bun.sh: ^1.4.1 as unsatisfiable while 1.4.1 and 1.4.2 were both published with darwin-arm64 builds. The bun branch also returned before ever reaching registryVersions, so the registry was never consulted for it.
The fix
Version lookups return per-source outcomes (
VersionSourceOutcome: source, versions, error), so a source that could not be consulted is distinguishable from one that answered nothing.Every origin has a fallback behind it. When the bun releases lookup errors or comes back empty, the registry is consulted in its place (same for nodejs.org; zig's origin-first precedence is preserved, since ziglang.org prunes dev builds). Reporting the failure was only half the fix — an unreachable source has to be replaced.
Failures name their sources, replacing
checked 143 versions, newest 1.4.0, a count that matched no registry endpoint and named no source:No version of bun.com satisfies "^1.4.1". 1 of 3 version sources could not be consulted, so this list is incomplete — the pin may well be satisfiable. Version sources consulted: - api.github.com/repos/oven-sh/bun/releases: unavailable (HTTP 403 …) - https://registry.pantry.dev/binaries/bun.com (darwin-arm64): 8 versions, newest 1.4.2 - bundled catalog (this pantry build): 145 versions, newest 1.4.0The silent half is now audible. Resolving while a source is down emits a new
onWarndiagnostic naming every source. It is deliberately separate fromquiet: the action passesquiet: true, and CI is exactly where a pin resolves off a stale snapshot.onWarnthreads through both branches ofinstallResolvedSystemPackagesand maps tocore.warning.resolveLatestVersionfor bun/node consults the registry before the bundled catalog, so@latestduring a rate-limited hour stops returning a months-old version. Both fall back to the catalog if the registry is unreachable, so a workingbun@latestcannot become a hard failure.New
github-tokeninput on the action, defaulting to$, exported in-process asPANTRY_GITHUB_TOKEN— ahead ofGITHUB_TOKENso the release path is untouched, and not exported to the job env. That removes the shared-IP rate limit as a failure mode in the first place.
Verification
Forcing a 401 on the GitHub lookup against the real registry:
bun.com: resolved "^1.4.1" to 1.4.4 with 1 of 3 version sources unavailable; a newer match may exist upstream
- api.github.com/repos/oven-sh/bun/releases: unavailable (HTTP 401 fetching …)
- https://registry.pantry.dev/binaries/bun.com (darwin-arm64): 10 versions, newest 1.4.4
^1.4.1 -> 1.4.4Tests: pure-composition coverage including the reproduction (^1.4.1 against rate-limited + stale-bundled resolves to nothing, then to the newest match once the registry is consulted), plus an integration test that mocks node:https into a 403 and serves registry metadata over loopback, asserting a version no bundled catalog could contain resolves anyway and that platform filtering is honoured. tsc and pickier clean.
Note: this branch is 157 commits behind main (it was rebased onto main on 2026-10-23 and pushed unrebased since). Say the word and I'll rebase it — the earlier rebase hit four conflicts where upstream had improved the same area, so it is worth doing attentively rather than blind.
🤖 Generated with Claude Code