jdx/mise · error

brew-cask:{}: cask metadata has no sha256

Error message

brew-cask:{}: cask metadata has no sha256

What it means

fetch_archive (src/system/packages/brew/cask.rs:1049) enforces download integrity: after fetching the cask artifact it verifies the sha256 from the cask metadata, honoring the explicit 'no_check' opt-out. If the cask JSON carries no sha256 stanza at all, mise refuses to install an unverified download rather than skip integrity checking by default.

Source

Thrown at src/system/packages/brew/cask.rs:1049

    let url_hash = &hash::hash_sha256_to_str(&cask.url)[..12];
    let archive = cache_dir.join(format!(
        "{}-{}-{url_hash}-{filename}",
        cask.token, cask.version
    ));
    if !archive.exists() {
        HTTP.download_file(&cask.url, &archive, pr).await?;
        // Strip macOS quarantine so it doesn't propagate into extracted/copied artifacts.
        let _ = std::process::Command::new("xattr")
            .args(["-d", "com.apple.quarantine"])
            .arg(&archive)
            .stdout(std::process::Stdio::null())
            .stderr(std::process::Stdio::null())
            .status();
    }
    match cask.sha256.as_deref() {
        Some("no_check") => {}
        Some(sha256) => hash::ensure_checksum(&archive, sha256, pr, "sha256")?,
        None => bail!("brew-cask:{}: cask metadata has no sha256", cask.token),
    }
    Ok(archive)
}

fn extract_archive(cask: &Cask, archive: &Path, pr: Option<&dyn SingleReport>) -> Result<PathBuf> {
    let extract_dir = crate::dirs::CACHE
        .join("system-brew")
        .join("cask-extract")
        .join(format!("{}-{}", cask.token, cask.version));
    file::remove_all(&extract_dir)?;
    file::create_dir_all(&extract_dir)?;
    let filename = archive
        .file_name()
        .and_then(|f| f.to_str())
        .unwrap_or_default();
    if is_dmg_archive(archive, filename)? {
        file::un_dmg(archive, &extract_dir)?;
    } else {

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. If you maintain the tap, add the sha256 stanza to the cask (compute with shasum -a 256 on the artifact), or set it explicitly to no_check if the download genuinely changes per fetch (installer stubs)
  2. Prefer the official homebrew/cask build of the app when one exists
  3. Do not work around by disabling verification — the guard protects against tampered or corrupted downloads

Example fix

// before (tap's api/cask/myapp.json)
{ "token": "myapp", "version": "1.0", "url": "https://vendor/myapp.dmg" }

// after
{ "token": "myapp", "version": "1.0", "url": "https://vendor/myapp.dmg", "sha256": "<64-hex digest>" }
Defensive patterns

Strategy: validation

Validate before calling

let cask = fetch_cask_json(token).await?;
match cask.sha256.as_deref() {
    Some(_) | Some("no_check") => { /* proceed to install */ }
    None => return Err(anyhow!("cask {token} ships no sha256 - refusing to install")),
}

Prevention

When it happens

Trigger: Installing a tapped cask whose api/cask/<token>.json omits the sha256 field. Official homebrew/cask metadata always carries sha256 or 'no_check', so this almost always comes from third-party or internal taps with hand-authored cask JSON.

Common situations: Internal taps generated by scripts that drop empty fields; quickly authored casks without checksums; repackaged vendor binaries where the packager skipped the shasum step.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/1b0f78f3c3a0994d. Report an issue: GitHub.