jdx/mise · error

brew-cask: font artifact

Error message

brew-cask: font artifact '{}' was not staged

What it means

link_font runs after stage_font should have copied the font into the caskroom. It requires caskroom_font.is_file() before performing the atomic swap into the user font directory (rename the old target aside, copy the new one in). If the caskroom path is missing or not a regular file at link time, it bails with the cask's font source name — protecting the swap from operating on nothing.

Solutions

  1. Uninstall the cask (or delete its caskroom version directory) and reinstall so stage_font runs cleanly end-to-end
  2. Verify the cask font source names an actual font file inside the archive
  3. Check the caskroom path from the error for a directory or symlink where the font should be, and clear it
  4. Re-run with MISE_DEBUG=1 to confirm whether stage_font executed at all

Example fix

# before: partial caskroom state
$ mise brew install --cask font-x   # link_font fails

# after
$ mise brew uninstall --cask font-x && mise brew install --cask font-x
Defensive patterns

Strategy: validation

Validate before calling

// After staging fonts, assert each caskroom font is a regular file before linking.
for font in &cask.artifacts.fonts {
    let staged = caskroom_font_path(caskroom, font)?;
    if !staged.is_file() {
        return Err(eyre!("font '{}' not staged", font.source));
    }
}

Type guard

fn staged_font_is_file(caskroom: &Path, font: &FontArtifact) -> bool {
    caskroom_font_path(caskroom, font).map(|p| p.is_file()).unwrap_or(false)
}

Prevention

When it happens

Trigger: stage_font never ran or its output vanished (caskroom cleaned mid-install); the font source resolved to a directory or symlink rather than a regular file; caskroom_font_path computed a different location than stage_font wrote (e.g. changed font target between staging and linking).

Common situations: Interrupted installs leaving partial caskroom state; cask font stanza pointing at a directory inside the archive; font archives with unexpected layouts.

Related errors


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

Appendix: source

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

    sudo::run("installer", &args, &[])
}

fn stage_font(stage: &Path, caskroom: &Path, font: &FontArtifact) -> Result<()> {
    let caskroom_font = caskroom_font_path(caskroom, font)?;
    file::remove_all(&caskroom_font)?;
    if let Some(parent) = caskroom_font.parent() {
        file::create_dir_all(parent)?;
    }
    let source = find_file_artifact(stage, &font.source)
        .ok_or_else(|| eyre!("brew-cask: font artifact '{}' was not found", font.source))?;
    copy_cask_artifact(&source, &caskroom_font)?;
    Ok(())
}

fn link_font(caskroom: &Path, font: &FontArtifact) -> Result<()> {
    let caskroom_font = caskroom_font_path(caskroom, font)?;
    if !caskroom_font.is_file() {
        bail!("brew-cask: font artifact '{}' was not staged", font.source);
    }
    let target = font_target_path(font)?;
    if let Some(parent) = target.parent() {
        file::create_dir_all(parent)?;
    }
    // Atomic swap: rename existing font aside before copying the new one so
    // that a failure during copy leaves the old font intact.
    let old_target = target.with_extension(format!(
        "mise-old-{}",
        crate::hash::hash_to_str(&target.display().to_string())
    ));
    file::remove_all(&old_target)?;
    if target.exists() {
        file::rename(&target, &old_target)?;
    }
    if let Err(e) = copy_cask_artifact(&caskroom_font, &target) {
        if old_target.exists() {
            let _ = file::rename(&old_target, &target);

View on GitHub (pinned to 6f52dcdf99)