{"record":{"id":"6f8b3e20ce3fbece","repo":"BoundaryML/baml","slug":"blob-path-has-no-parent","errorCode":null,"errorMessage":"blob path has no parent","messagePattern":"blob path has no parent","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"baml_language/crates/bex_events/src/value/artifact.rs","lineNumber":132,"sourceCode":"        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        })?;\n        fs::create_dir_all(parent)?;\n        let tmp_path = parent.join(format!(\n            \".{}.{}.{}.tmp\",\n            blob_ref.digest,\n            std::process::id(),\n            uuid::Uuid::new_v4()\n        ));\n        {\n            let mut tmp = OpenOptions::new()\n                .write(true)\n                .create_new(true)\n                .open(&tmp_path)?;\n            tmp.write_all(bytes)?;\n            tmp.flush()?;\n            tmp.sync_all()?;\n        }\n        if path.is_file() {","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bex_events/src/value/artifact.rs#L114-L150","documentation":"BlobStore::write_blob calls Path::parent() on the target blob path and errors if it returns None. A canonical content-addressed path (<root>/<algorithm>/<2-hex>/<digest>.blob) always has a parent, so this is a defensive guard; it would only trigger for a degenerate path such as a bare relative filename with no directory components.","triggerScenarios":"write_blob() where the resolved blob path has no parent directory component — effectively only if the blob store root itself is empty/degenerate or the path was reduced to a single file name.","commonSituations":"Constructing a BlobStore with an empty or malformed root so the joined path collapses, or misusing internal APIs to bypass path_for().","solutions":["Initialize the store with a proper non-empty root via BlobStore::new or ::with_boundary_dir (e.g. <dir>/blobs).","Ensure the root path is absolute or correctly relative to the working directory before writing blobs.","If it occurs with a valid store, treat it as a bug — the library should never reach this with normal path_for() output."],"exampleFix":"// before\nlet store = BlobStore::new(\"\")?; // degenerate path\n// after\nlet store = BlobStore::with_boundary_dir(\"/path/to/run_dir\")?; // <dir>/blobs","handlingStrategy":"validation","validationCode":"let root = Path::new(run_dir).join(\"blobs\");\nassert!(!root.as_os_str().is_empty(), \"blob store root must be non-empty\");\nfs::create_dir_all(&root)?;","typeGuard":null,"tryCatchPattern":"match store.write_blob(&blob_ref) {\n    Ok(r) => r,\n    Err(e) if e.kind() == io::ErrorKind::InvalidInput => {\n        eprintln!(\"degenerate blob path — check BlobStore root: {e}\");\n        return Err(e.into());\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Construct the store with BlobStore::with_boundary_dir or BlobStore::new with a valid directory.","Ensure the run directory exists and the process has write access before writing blobs."],"tags":["rust","blob-storage","filesystem","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"}