jdx/mise · error

invalid recovery content identifier

Error message

invalid recovery content identifier

What it means

validate_blob_id requires a content identifier to be exactly 64 ASCII hex characters (a SHA-256 hex digest). Recovery refuses to use a blob reference that does not look like a valid hash, preventing path injection into the blobs directory. Called by read_blob (when loading preimage content) and discard_except (when deleting sidecar blobs).

Source

Thrown at src/system/history/recovery.rs:123

    }
    restore(state_dir, path, prior)
}

fn validate_destination(path: &Path) -> Result<()> {
    if !path.is_absolute() || path.components().any(|c| matches!(c, Component::ParentDir)) {
        bail!("invalid recovery destination");
    }
    for parent in path.ancestors().skip(1) {
        if std::fs::symlink_metadata(parent).is_ok_and(|meta| meta.is_symlink()) {
            bail!("a parent directory is now a symlink; left untouched");
        }
    }
    Ok(())
}

fn validate_blob_id(hash: &str) -> Result<()> {
    if hash.len() != 64 || !hash.bytes().all(|b| b.is_ascii_hexdigit()) {
        bail!("invalid recovery content identifier");
    }
    Ok(())
}

pub(super) fn read_blob(state_dir: &Path, blob: &Blob) -> Result<Vec<u8>> {
    use base64::Engine;
    validate_blob_id(&blob.sha256)?;
    let bytes = match &blob.inline {
        Some(inline) => base64::engine::general_purpose::STANDARD.decode(inline)?,
        None => {
            let path = super::journal::blobs_dir_in(state_dir).join(&blob.sha256);
            let metadata = std::fs::symlink_metadata(&path)?;
            if !metadata.is_file() || metadata.len() != blob.size {
                bail!("invalid recovery content file");
            }
            std::fs::read(path)?
        }
    };

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Inspect the journal/pending JSON in the state directory and fix or remove the entry with the malformed hash.
  2. Delete the corrupt pending-operation record and accept the files' current contents (`recover <operation> --keep-current` or manual cleanup).
  3. Restore the state directory from backup if multiple records are corrupted.

Example fix

// before (corrupt blob id)
"sha256": "abc123"

// after (full 64-char SHA-256 hex digest)
"sha256": "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"
Defensive patterns

Strategy: validation

Validate before calling

fn looks_like_sha256(s: &str) -> bool {
    s.len() == 64 && s.bytes().all(|b| b.is_ascii_hexdigit())
}
// assert every blob id in the journal before invoking recovery

Try / catch

match result { Err(e) if e.to_string().contains("invalid recovery content identifier") => /* journal corrupt; discard the pending record */, other => other? }

Prevention

When it happens

Trigger: read_blob or discard_except encounters a Blob.sha256 that is empty, truncated, longer than 64 chars, or contains non-hex characters — i.e. corrupt, hand-edited, or maliciously crafted journal/pending data.

Common situations: A state directory damaged by disk failure or partial write, a journal file edited by hand, or future/older schema versions writing different hash formats.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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