{"record":{"id":"eec1c4942f085426","repo":"jdx/mise","slug":"the-release-list-of-packslip-project-has-sequenc","errorCode":null,"errorMessage":"the release list of packslip:{project} has sequence {sequence}, but sequence {last} was already accepted; refusing to go back","messagePattern":"the release list of packslip:(.+?) has sequence (.+?), but sequence (.+?) was already accepted; refusing to go back","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/packslip_pins.rs","lineNumber":213,"sourceCode":"        pins.pins.insert(project.to_string(), updated.clone());\n        save(path, &pins)?;\n    }\n    Ok(updated)\n}\n\n/// Refuse a release list whose sequence is below one already accepted for\n/// the project, and remember the highest seen.\npub(crate) fn check_sequence(project: &str, sequence: u64) -> Result<()> {\n    check_sequence_at(&pins_file(), project, sequence)\n}\n\npub(crate) fn check_sequence_at(path: &Path, project: &str, sequence: u64) -> Result<()> {\n    let _lock = locked(path)?;\n    let mut pins = load(path)?;\n    if let Some(last) = pins.sequences.get(project).copied()\n        && sequence < last\n    {\n        bail!(\n            \"the release list of packslip:{project} has sequence {sequence}, but sequence {last} was already accepted; refusing to go back\"\n        );\n    }\n    if pins.sequences.get(project) != Some(&sequence) {\n        pins.sequences.insert(project.to_string(), sequence);\n        save(path, &pins)?;\n    }\n    Ok(())\n}\n\n/// An absent supplementary list is allowed only before this machine has\n/// accepted one. Its disappearance must not undo signed withdrawals.\npub(crate) fn check_missing_list(project: &str) -> Result<()> {\n    check_missing_list_at(&pins_file(), project)\n}\n\nfn check_missing_list_at(path: &Path, project: &str) -> Result<()> {\n    let _lock = locked(path)?;","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/packslip_pins.rs#L195-L231","documentation":"mise caches the highest release-list sequence number it has ever accepted for each packslip project in a pins file. If a later fetched release list carries a sequence number lower than the cached one, this error is thrown because accepting it could roll back to an outdated or attacker-supplied list.","triggerScenarios":"Calling packslip release-list verification (via check_sequence -> check_sequence_at) when the remote list's sequence number is lower than the value stored in the local pins file for that project.","commonSituations":"A registry downgraded or replaced its signed release list; a mirror is serving a stale cached list; the local pins file was populated from a newer mirror earlier; a compromised CDN serves an older signed manifest.","solutions":["Update the registry's signed release list to a sequence number >= the locally accepted one","Refresh from the canonical host that published the higher-sequence list","Delete the pins file entry to re-baseline (only if you trust the lower list, e.g. intentional registry reset)","Wait for the registry to publish a new list with a higher sequence"],"exampleFix":"// before: mixing a stale mirror with a newer canonical list\nMISE_PACKSLIP_MIRROR=https://stale-mirror.example.com/ mise install aqua:org/tool\n// after: use the canonical host that has the highest sequence\nmise install aqua:org/tool","handlingStrategy":"validation","validationCode":"let pins = read_pins_file()?;\nif let Some(last) = pins.sequences.get(project) {\n    let seq = fetch_list_sequence(project)?;\n    if seq < *last { eprintln!(\"list would roll back {last} -> {seq}; refresh mirror first\"); }\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"refusing to go back\") => refresh_from_canonical_host(),\n    other => other?,\n}","preventionTips":["Always fetch release lists from the canonical host, not stale mirrors","Don't share pins files between machines with different mirror priorities","Monitor registry for deliberate list resets","If a reset is intended, clear the pins entry first"],"tags":["security","rollback-protection","packslip","registry"],"backgroundTag":"invalid-state-transition","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"}