Hmbown/CodeWhale · error · anyhow::Error

installed package failed content digest validation: {err}

Error message

installed package failed content digest validation: {err}

What it means

At the end of a skill-package install, the installer computes a content digest over the installed tree (compute_package_digest on final_path) so the .installed-from v2 marker can pin it. If digest computation itself fails (the wrapped {err}), the install is rolled back: final_path is removed, any pre-existing backup directory is renamed back into place, and this error is returned. It signals a filesystem-level failure while walking/reading the just-installed package, not a mismatch against an expected checksum.

Source

Thrown at crates/tui/src/skills/install.rs:377

        if let Some(parent) = final_path.parent() {
            fs::create_dir_all(parent).with_context(|| {
                format!("failed to create skills directory {}", parent.display())
            })?;
        }
        fs::rename(&staged.staged_path, &final_path).context("failed to install staged skill")?;
    }

    // Write the marker last so a partial install never leaves a stale
    // .installed-from on disk. Prefer v2 with package content digest.
    let spec = source_spec_string(&source);
    let content_digest = match super::package_digest::compute_package_digest(&final_path) {
        Ok(digest) => digest,
        Err(err) => {
            let _ = fs::remove_dir_all(&final_path);
            if let Some(backup) = backup_path.take() {
                let _ = fs::rename(&backup, &final_path);
            }
            return Err(anyhow::anyhow!(
                "installed package failed content digest validation: {err}"
            ));
        }
    };
    if let Err(err) = write_installed_from_v2(
        &final_path,
        &spec,
        Some(&source_url),
        &checksum,
        &content_digest,
        &staged.skill_name,
    ) {
        let _ = fs::remove_dir_all(&final_path);
        if let Some(backup) = backup_path.take() {
            let _ = fs::rename(&backup, &final_path);
        }
        return Err(err);
    }

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Read the wrapped {err} — it names the exact file and I/O cause.
  2. Ensure no concurrent installs target the same skill (serialize installs or re-run the install once the other finishes).
  3. Check disk space and directory permissions on the skills root.
  4. On Windows, exclude the skills directory from antivirus scanning or pause watchers, then reinstall.
Defensive patterns

Strategy: retry

Try / catch

match install_skill(&source) {
    Ok(_) => {}
    Err(e) if e.to_string().contains("content digest validation") => {
        // install was rolled back; inspect underlying cause, clear contention, retry once
        log::warn!("skill install rolled back: {e:#}");
        std::thread::sleep(std::time::Duration::from_secs(1));
        install_skill(&source)?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: compute_package_digest fails while walking final_path: a file becomes unreadable mid-walk (permissions changed), the tree is mutated concurrently by another install of the same skill, a path disappears between staging and digest, or an I/O error (full disk, failing drive, AV/lock interference) occurs.

Common situations: Two installs racing into the same skills directory; antivirus or file locks on Windows touching freshly written files; disk-full during a large package install; a watcher process pruning skill files as they land.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/68b92f87c0fbf0d4. Report an issue: GitHub.