jdx/mise · error
the release list expired at
Error message
the release list expired at {}; the vendor has not published a fresh one What it means
After cryptographically verifying the vendor's signed release list, mise also checks its freshness via the list's expires_at predicate. If the list's expiry timestamp is in the past, the vendor has not published a fresh signed list, and mise refuses to use the stale one even though its signature is valid.
Solutions
- Wait for the vendor to publish a fresh release list, then retry.
- Check the vendor's status page or repo for a publishing outage.
- If you control the packslip, regenerate and republish the signed list with a new expiry.
- As a stopgap, pin a version already present in your mise.lock whose artifacts were verified previously.
Defensive patterns
Strategy: retry
Validate before calling
// Check expiry before attempting install.
if (new Date(list.predicate.expires_at) < new Date()) {
console.warn('release list expired; vendor must publish a fresh list');
} Try / catch
try {
await miseInstall(tool);
} catch (e) {
if (String(e).includes('release list expired')) {
await sleep(retryDelay); // vendor usually republishes within hours
await miseInstall(tool);
} else throw e;
} Prevention
- Subscribe to vendor status/announcement channels to learn of list-publishing outages.
- Keep already-locked versions installed so outages don't block existing environments.
- Prefer vendors whose publishing automation is demonstrably reliable.
- Don't pin hopes on mirrors — the signature and expiry are checked regardless of source.
When it happens
Trigger: release_list, github_list, or fetch when packslip::verify_release_list succeeds but verified.list.is_current(now) is false, i.e. predicate.expires_at < now.
Common situations: A vendor's release-list publishing pipeline broke and stopped refreshing the signed list; a mirror or cache serving an old list; using a tool whose vendor went dormant.
Related errors
- published a stamp list for before (sequence ) but now…
- mise.lock says signed , but this release is signed by …
- packslip: @ : manifest digest differs from signed list
- packslip: : this release . If the vendor announced the…
- packslip: @ was withdrawn by the vendor
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/34eae939b8877374.
Report an issue: GitHub.
Appendix: source
Thrown at src/backend/packslip.rs:621
packslip::verify(bundle, &pin.trust(), options, artifacts).map_err(|e| eyre!("{e}"))
})
}
pub(crate) fn verify_release_list(
bundle: &str,
pin: &Pin,
require_log: bool,
) -> Result<ReleaseListStatement> {
file::run_blocking(|| {
let root = packslip::sigstore::trusted_root(None).map_err(|e| eyre!("{e}"))?;
let options = packslip::Options {
require_log,
trusted_root: &root,
};
let verified = packslip::verify_release_list(bundle, &pin.trust(), options)
.map_err(|e| eyre!("{e}"))?;
if !verified.list.is_current(jiff::Timestamp::now()) {
bail!(
"the release list expired at {}; the vendor has not published a fresh one",
verified.list.predicate.expires_at
);
}
Ok(verified.list)
})
}
/// The headers a download from GitHub needs; nothing for anywhere else.
/// Listing timestamps only filter candidates. The authenticated log time
/// decides whether a selected release is old enough to install.
/// Whether the release-age cutoff has already had its say about this version.
///
/// The cutoff decides which release a fuzzy request may pick. A lockfile entry
/// and an explicit pin like `hk = "2.0.1"` are both that decision already taken
/// and committed, so re-applying the cutoff here would reject a version mise
/// itself resolved — `minimum_release_age` documents both as unfiltered.
fn selection_already_made(tv: &ToolVersion) -> bool {View on GitHub (pinned to 533346cc37)