{"record":{"id":"34eae939b8877374","repo":"jdx/mise","slug":"the-release-list-expired-at-the-vendor-has-not","errorCode":null,"errorMessage":"the release list expired at {}; the vendor has not published a fresh one","messagePattern":"the release list expired at (.+?); the vendor has not published a fresh one","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/backend/packslip.rs","lineNumber":517,"sourceCode":"        packslip::verify(bundle, &pin.trust(), options, artifacts).map_err(|e| eyre!(\"{e}\"))\n    })\n}\n\npub(crate) fn verify_release_list(\n    bundle: &str,\n    pin: &Pin,\n    require_log: bool,\n) -> Result<ReleaseListStatement> {\n    file::run_blocking(|| {\n        let root = packslip::sigstore::trusted_root(None).map_err(|e| eyre!(\"{e}\"))?;\n        let options = packslip::Options {\n            require_log,\n            trusted_root: &root,\n        };\n        let verified = packslip::verify_release_list(bundle, &pin.trust(), options)\n            .map_err(|e| eyre!(\"{e}\"))?;\n        if !verified.list.is_current(jiff::Timestamp::now()) {\n            bail!(\n                \"the release list expired at {}; the vendor has not published a fresh one\",\n                verified.list.predicate.expires_at\n            );\n        }\n        Ok(verified.list)\n    })\n}\n\n/// The headers a download from GitHub needs; nothing for anywhere else.\n/// Listing timestamps only filter candidates. The authenticated log time\n/// decides whether a selected release is old enough to install.\nfn check_verified_age(\n    logged_at: Option<&str>,\n    published_at: &str,\n    before: Option<jiff::Timestamp>,\n) -> Result<()> {\n    let Some(before) = before else {\n        return Ok(());","sourceCodeStart":499,"sourceCodeEnd":535,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/backend/packslip.rs#L499-L535","documentation":"After cryptographically verifying the vendor's signed release list, mise additionally checks that the list is still temporally valid via `is_current`. If the list's `predicate.expires_at` timestamp has passed, the signature may be valid but the attestation is stale — the vendor must publish a fresh signed list. mise refuses to proceed with an expired list rather than trusting outdated withdrawal/version data.","triggerScenarios":"Calling `release_list`, `github_list`, or `fetch` for a packslip project when `verified.list.is_current(Timestamp::now())` returns false, i.e. the current time is past `predicate.expires_at` of the fetched release list.","commonSituations":"A vendor stops publishing fresh release lists (project abandoned or transparency log outage); a stale mirror or cache serves an old list; system clock skew makes the local time appear past the expiry.","solutions":["Wait for the vendor to publish a fresh signed release list, then retry `mise install`","Refresh mirrors/cache so you fetch the latest list, or clear any cached release list","Check the local clock (NTP sync) if expiry seems wrong; report the stale list to the vendor if they are active"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"match mise::install(tool) {\n    Err(e) if e.to_string().contains(\"release list expired\") => {\n        schedule_retry_after_vendor_republish();\n    }\n    other => other?,\n}","preventionTips":["Prefer actively maintained vendors that republish lists regularly","Keep mirrors/cache fresh so you always fetch the newest list","Keep clocks NTP-synced to avoid false expiry","Watch vendor status pages for release-list publication outages"],"tags":["packslip","expiry","supply-chain"],"backgroundTag":"jwt-token-expired","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}