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
- Inspect the journal/pending JSON in the state directory and fix or remove the entry with the malformed hash.
- Delete the corrupt pending-operation record and accept the files' current contents (`recover <operation> --keep-current` or manual cleanup).
- 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
- Never hand-edit journal or blob metadata files
- Back up the state directory so corrupted records can be restored
- Keep the mise version consistent; mixing versions writing different record formats can corrupt state
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
- invalid task action identity
- invalid blob digest (expected sha256: prefix): {digest}
- invalid blob digest (expected 64 lowercase hex chars): {dige
- invalid Hex OTP checksum for {release_tag}: {checksum}
- signed mise release checksum manifest contains an invalid SH
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/09fdf86fe7eedb20.
Report an issue: GitHub.