jdx/mise · critical

verified checksum file digest does not match expected…

Error message

verified checksum file digest does not match expected checksum for {artifact_filename}

What it means

After verifying an aqua artifact's checksum via a checksum file, the computed digest of the checksum file itself is compared against an expected checksum when one is configured with the same algorithm. A mismatch means the checksum file (and hence possibly the artifact) differs from what was expected — a supply-chain/integrity guard — so the install aborts.

Solutions

  1. Verify you are pinned to the intended version and that the expected checksum was copied correctly from the upstream release
  2. Check the upstream release for a re-tag/re-upload; if checksums legitimately changed, update the pinned expected checksum in the lockfile/config
  3. Retry later or use a different mirror in case of a corrupted download, then re-verify
  4. Treat as a potential supply-chain issue: do not bypass; if you cannot confirm integrity, do not install the artifact

Example fix

# before (stale pin in mise.lock after upstream re-upload)
checksum = "sha256:1111..."
# after: recompute from the verified upstream checksums file and update the pin
checksum = "sha256:def4..."
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the pin against the upstream release before installing:
// fetch the release's published checksums file and compare the expected digest
let upstream = fetch_checksums_file(release_tag).await?;
if upstream.get(algorithm) != Some(&expected_digest) {
    eprintln!("pinned checksum differs from upstream release; investigate before installing");
}

Try / catch

match verify_checksum_file(&artifact, &expected) {
    Err(e) if e.to_string().contains("does not match expected checksum") => {
        // treat as a potential supply-chain event: block the install and alert
        report_integrity_failure(&artifact_filename, e);
    }
    r => r?,
}

Prevention

When it happens

Trigger: Installing an aqua package where the config (or lockfile) pins an expected checksum for the checksum file, the algorithm matches the computed one, but the computed `algo:digest` string differs from the expected value.

Common situations: Upstream project re-uploaded or regenerated its checksums file for the same release tag; a proxy/mirror served a stale or tampered checksums file; the pin in mise.lock or package config was made for a different version/platform; typo when hand-pinning the expected checksum.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/d8943f6b651bdc73. Report an issue: GitHub.

Appendix: source

Thrown at src/backend/aqua.rs:2068

    fn verify_checksum_file_matches_expected(
        &self,
        checksum_config: &AquaChecksum,
        checksum_path: &Path,
        artifact_filename: &str,
        expected_checksum: Option<&str>,
    ) -> Result<()> {
        let checksum_content = file::read_to_string_bom(checksum_path)?;
        let checksum_str = self.parse_checksum_from_content(
            &checksum_content,
            checksum_config,
            artifact_filename,
        )?;
        let checksum_val = format!("{}:{}", checksum_config.algorithm(), checksum_str);
        if let Some(expected) = expected_checksum
            && same_checksum_algorithm(expected, &checksum_val)
            && expected != checksum_val
        {
            bail!(
                "verified checksum file digest does not match expected checksum for {artifact_filename}"
            );
        }
        Ok(())
    }

    pub(crate) fn from_arg(ba: BackendArg) -> Self {
        let full = ba.full_without_opts();
        let mut id = full.split_once(":").unwrap_or(("", &full)).1;
        if !id.contains("/") {
            id = REGISTRY
                .get(id)
                .and_then(|t| t.backends.iter().find_map(|s| s.full.strip_prefix("aqua:")))
                .unwrap_or_else(|| {
                    warn!("invalid aqua tool: {}", id);
                    id
                });
        }

View on GitHub (pinned to 533346cc37)