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

  1. Wait for the vendor to publish a fresh release list, then retry.
  2. Check the vendor's status page or repo for a publishing outage.
  3. If you control the packslip, regenerate and republish the signed list with a new expiry.
  4. 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

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


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)