{"record":{"id":"2ee5557ac5ab499a","repo":"BoundaryML/baml","slug":"blob-digest-has-no-prefix","errorCode":null,"errorMessage":"blob digest has no prefix","messagePattern":"blob digest has no prefix","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"baml_language/crates/bex_events/src/value/artifact.rs","lineNumber":115,"sourceCode":"}\n\nimpl BlobStore {\n    #[must_use]\n    pub fn new(root: impl AsRef<Path>) -> Self {\n        Self {\n            root: root.as_ref().to_path_buf(),\n        }\n    }\n\n    #[must_use]\n    pub fn for_boundary_dir(boundary_dir: impl AsRef<Path>) -> Self {\n        Self::new(boundary_dir.as_ref().join(\"blobs\"))\n    }\n\n    pub fn path_for(&self, blob_ref: &BlobRef) -> io::Result<PathBuf> {\n        let digest = blob_ref.normalized_digest()?;\n        let prefix = digest.get(..2).ok_or_else(|| {\n            io::Error::new(io::ErrorKind::InvalidData, \"blob digest has no prefix\")\n        })?;\n        Ok(self\n            .root\n            .join(&blob_ref.algorithm)\n            .join(prefix)\n            .join(format!(\"{digest}.blob\")))\n    }\n\n    pub fn write_blob(&self, bytes: &[u8]) -> io::Result<BlobRef> {\n        let blob_ref = BlobRef::sha256(bytes);\n        let path = self.path_for(&blob_ref)?;\n        if path.is_file() {\n            return Ok(blob_ref);\n        }\n\n        let parent = path.parent().ok_or_else(|| {\n            io::Error::new(io::ErrorKind::InvalidInput, \"blob path has no parent\")\n        })?;","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bex_events/src/value/artifact.rs#L97-L133","documentation":"BlobStore::path_for sharding uses the first two hex characters of the normalized digest as a subdirectory prefix. This error fires when slicing digest[..2] fails, i.e. the digest has fewer than 2 characters. In practice validate() (length 64, hex only) runs first, so this is a defensive internal invariant guard.","triggerScenarios":"path_for() with a BlobRef whose digest is shorter than 2 characters — only possible if validate/normalized_digest were bypassed or a new code path constructs the store without calling normalized_digest.","commonSituations":"Custom or forked code paths calling path_for with an unvalidated BlobRef, or regressions after refactoring validation order.","solutions":["Ensure the digest passes normalized_digest() before computing the path; fix the caller to call validate().","Check that the BlobRef construction path produces the full 64-char digest, not an empty string.","If this appears with valid digests, report it — it indicates an internal invariant violation in the library."],"exampleFix":"// before\nlet prefix = digest.get(..2).ok_or(...)?; // reached with digest.len() < 2\n// after\nlet digest = blob_ref.normalized_digest()?; // validates len==64 + hex first\nlet prefix = &digest[..2];","handlingStrategy":"validation","validationCode":"let digest = blob_ref.normalized_digest()?; // panics-free guard: validates len 64 + hex before path math\ndebug_assert!(digest.len() >= 2);","typeGuard":"fn has_prefix(digest: &str) -> bool { digest.len() >= 2 }","tryCatchPattern":"let path = store.path_for(&blob_ref).map_err(|e| {\n    eprintln!(\"cannot resolve blob path: {e}\");\n    e\n})?;","preventionTips":["Always call normalized_digest()/validate() before using a BlobRef in path computations.","Use the public BlobStore API (write_blob/read_blob) instead of hand-assembling paths."],"tags":["rust","blob-storage","internal-invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}