zeroclaw-labs/zeroclaw · error · anyhow::Error
plugin archive sha256 mismatch
Error message
plugin archive sha256 mismatch
What it means
Integrity check failure: the SHA-256 of the downloaded archive bytes does not match the digest recorded in the registry entry (after stripping an optional 'sha256:' prefix, compared case-insensitively). verify_sha256_if_present only runs when the entry supplies a digest — no digest means no check.
Source
Thrown at src/plugin_registry.rs:157
Ok(bytes)
}
fn append_chunk_capped(bytes: &mut Vec<u8>, chunk: &[u8], max_bytes: usize) -> Result<()> {
if bytes.len().saturating_add(chunk.len()) > max_bytes {
bail!("plugin archive exceeds maximum size of {max_bytes} bytes");
}
bytes.extend_from_slice(chunk);
Ok(())
}
fn verify_sha256_if_present(bytes: &[u8], expected: Option<&str>) -> Result<()> {
let Some(expected) = expected else {
return Ok(());
};
let expected = expected.strip_prefix("sha256:").unwrap_or(expected);
let actual = hex::encode(Sha256::digest(bytes));
if !actual.eq_ignore_ascii_case(expected) {
bail!("plugin archive sha256 mismatch");
}
Ok(())
}
pub(crate) fn extract_zip_safe<R>(reader: R, dest: &Path) -> Result<PathBuf>
where
R: Read + Seek,
{
extract_zip_safe_with_limit(reader, dest, MAX_PLUGIN_EXTRACTED_BYTES)
}
fn extract_zip_safe_with_limit<R>(
reader: R,
dest: &Path,
max_extracted_bytes: u64,
) -> Result<PathBuf>
where
R: Read + Seek,View on GitHub (pinned to 88bb9c8533)
Solutions
- Re-fetch the registry index — if it is stale it references an old digest, and a fresh index matches the current artifact
- If you publish the plugin, recompute sha256sum of the artifact and republish the entry with it in the same change
- Verify manually: sha256sum downloaded.zip vs the entry's value to distinguish corruption from mismatch
- If both index and artifact agree yet it still fails, suspect a truncating proxy; download from a trusted network and compare
Example fix
# before # entry: sha256 = "aa11..." but artifact bytes hash to "bb22..." # after sha256sum p-0.3.0.zip # bb22... # update index entry to "bb22..." (or re-upload the aa11... artifact) and retry
Defensive patterns
Strategy: retry
Validate before calling
// Pre-verify before install when you control the pipeline:
let digest = hex::encode(Sha256::digest(&archive_bytes));
if !digest.eq_ignore_ascii_case(entry_sha256.trim_start_matches("sha256:")) {
anyhow::bail!("digest mismatch before install");
} Type guard
fn digest_matches(actual_hex: &str, expected: &str) -> bool {
actual_hex.eq_ignore_ascii_case(expected.trim_start_matches("sha256:"))
} Try / catch
// On sha256 mismatch: refresh the registry index once and retry (stale index // is the common cause). A second identical failure means the artifact itself // changed — stop and report to the publisher; do not bypass the check.
Prevention
- Always publish sha256 with registry entries
- Regenerate digests in the same commit as artifact uploads
- Treat persistent mismatches as a security incident, not an inconvenience
When it happens
Trigger: Calling plugin install where the registry entry has a sha256 field but the artifact at the URL has different bytes: artifact was rebuilt/overwritten without updating the index, truncated/corrupted download, or the URL now serves different content.
Common situations: Publisher bumped the artifact but forgot to regenerate the digest; CDN cache serving an older artifact than the index references; a genuinely tampered/replaced artifact (the case this guard exists for).
Related errors
- purge_session not supported by this memory backend
- plugin archive exceeds maximum size of {MAX_PLUGIN_ZIP_BYTES
- plugin archive exceeds maximum size of {max_bytes} bytes
- plugin archive exceeds extracted size limit of {max_extracte
- plugin archive contains unsafe path: {raw_name}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/f3fd7240d9d6ec34.
Report an issue: GitHub.