linera-io/linera-protocol · error · anyhow

Invalid blob ID: {s}

Error message

Invalid blob ID: {s}

What it means

BlobId::from_str expects exactly '<BlobType>:<CryptoHash>' (e.g. 'Data:6ab0f8e8...'). The string is split on ':' and must produce exactly 2 parts; any other part count raises this error. If the count is 2 but the type or hash is malformed, you instead get the chained 'Invalid BlobType!' or 'Invalid hash!' context errors, so this specific message means the overall shape is wrong.

Source

Thrown at linera-base/src/identifiers.rs:352

    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", self.blob_type, self.hash)?;
        Ok(())
    }
}

impl std::str::FromStr for BlobId {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts = s.split(':').collect::<Vec<_>>();
        if parts.len() == 2 {
            let blob_type = BlobType::from_str(parts[0]).context("Invalid BlobType!")?;
            Ok(BlobId {
                hash: CryptoHash::from_str(parts[1]).context("Invalid hash!")?,
                blob_type,
            })
        } else {
            Err(anyhow!("Invalid blob ID: {s}"))
        }
    }
}

#[derive(Serialize, Deserialize)]
#[serde(rename = "BlobId")]
struct BlobIdHelper {
    hash: CryptoHash,
    blob_type: BlobType,
}

impl Serialize for BlobId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        if serializer.is_human_readable() {
            serializer.serialize_str(&self.to_string())

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Use the exact Display form: format!("{}:{}", blob_type, crypto_hash)
  2. Round-trip through BlobId::to_string() instead of building strings by hand
  3. If your source data uses a different separator (e.g. '/'), convert it by splitting once on that separator and re-joining with ':'

Example fix

// before: bare hash has no 'type:' prefix
let blob_id: BlobId = hash_str.parse()?; // Err: Invalid blob ID: 6ab0f8e8...

// after: type-prefixed Display form
let blob_id: BlobId = format!("Data:{hash_str}").parse()?;
Defensive patterns

Strategy: type-guard

Validate before calling

fn looks_like_blob_id(s: &str) -> bool {
    let Some((ty, hash)) = s.split_once(':') else { return false };
    !ty.is_empty()
        && hash.len() == 64
        && hash.bytes().all(|b| b.is_ascii_hexdigit())
}

Type guard

fn parse_blob_id(s: &str) -> Option<BlobId> {
    s.parse().ok()
}

Prevention

When it happens

Trigger: Calling \"...\".parse::<BlobId>() on a string with zero colons or two-or-more colons: a bare hash like '6ab0f8e8...', a type alone like 'Data', or a string where the hash portion itself contains ':' separators.

Common situations: Hand-assembling blob IDs in glue code or shell scripts; passing a blob URL or a 'type/hash' string produced by another tool; copy-paste truncation from CLI output; passing a serialized JSON object where the Display string is expected.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/f4f393970407c922. Report an issue: GitHub.