{"record":{"id":"7a16cc2726b9cf99","repo":"BoundaryML/baml","slug":"compiled-output-has-no-unit-for-rel","errorCode":null,"errorMessage":"compiled output has no unit for `{rel}`","messagePattern":"compiled output has no unit for `(.+?)`","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"baml_language/crates/baml_cli/src/bytecode_cache.rs","lineNumber":1610,"sourceCode":"        }\n\n        let user_files = user_files_with_rel_paths(db, package);\n        // Only a successful reuse compile returns assembled `units`. A full\n        // fallback must persist freshly decomposed units for every file rather\n        // than carrying pointers from the abandoned reuse plan.\n        let pointer_plan = if reused_units { plan } else { None };\n        let mut unit_keys = HashMap::with_capacity(user_files.len());\n        let mut unit_entries_written = 0usize;\n        for (_, rel) in &user_files {\n            if let Some(key) = pointer_plan\n                .filter(|plan| plan.clean_files.contains(rel))\n                .and_then(|plan| plan.unit_keys.get(rel))\n            {\n                unit_keys.insert(rel.clone(), *key);\n                continue;\n            }\n            let Some(unit) = units_by_source.get(rel.as_str()) else {\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::InvalidData,\n                    format!(\"compiled output has no unit for `{rel}`\"),\n                ));\n            };\n            if std::path::Path::new(&unit.source_file).is_absolute() {\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::InvalidData,\n                    format!(\"unit source path is not root-relative: `{rel}`\"),\n                ));\n            }\n            let (key, wrote) = self.cache.store_unit_shared(unit)?;\n            unit_entries_written += usize::from(wrote);\n            unit_keys.insert(rel.clone(), *key.as_bytes());\n        }\n        cache_debug(format_args!(\n            \"unit store: wrote {unit_entries_written}, reused {}\",\n            user_files.len().saturating_sub(unit_entries_written)\n        ));","sourceCodeStart":1592,"sourceCodeEnd":1628,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/baml_cli/src/bytecode_cache.rs#L1592-L1628","documentation":"While persisting unit artifacts, the cache walks every current user file and needs either a pointer from the reuse plan (for clean files) or an assembled CompilationUnit keyed by the file's root-relative path. If neither exists for `rel`, the compiled output is missing a unit that the project requires, so the store is aborted as corrupt state rather than writing an incomplete manifest.","triggerScenarios":"store_artifacts_with_manifest is called with CompiledArtifacts whose units slice (or the ReusePlan's unit_keys, for clean files) does not cover a file returned by user_files_with_rel_paths — e.g. units were assembled from a stale file list, the plan's clean_files contains a rel path absent from both plan.unit_keys and units, or units were built before a file was added to the project.","commonSituations":"Files added/renamed mid-build while a reuse plan from a previous compile is in play; an out-of-sync or corrupted cache manifest from an interrupted run; a tooling bug that enumerates user files after units were assembled.","solutions":["Delete the baml bytecode cache directory and rebuild cleanly so no reuse plan or stale units are replayed.","Make sure no build process mutates the project's .baml files (add/rename/delete) while a compile is in flight.","Re-run from a clean checkout if a stale generated tree or leftover manifest is suspected.","If it reproduces deterministically on a clean cache, file a bug with the repro — the unit assembly / reuse-plan pairing is violating its contract."],"exampleFix":"// before: reusing a plan assembled before new files were added\nstore_artifacts_with_manifest(&db, pkg, &compiled, &fresh, Some(&stale_plan))\n// after: fall back to a full compile so all units are freshly assembled\nstore_artifacts_with_manifest(&db, pkg, &compiled, &fresh, None)","handlingStrategy":"validation","validationCode":"let unit_sources: HashSet<&str> = compiled.units.iter().map(|u| u.source_file.as_str()).collect();\nfor (_, rel) in &user_files {\n    assert!(unit_sources.contains(rel.as_str()), \"no assembled unit for {rel}\");\n}","typeGuard":"fn units_cover_all_files(units: &[CompilationUnit], rels: &[String]) -> bool {\n    let have: HashSet<&str> = units.iter().map(|u| u.source_file.as_str()).collect();\n    rels.iter().all(|r| have.contains(r.as_str()))\n}","tryCatchPattern":"match result {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidData\n        && e.to_string().contains(\"compiled output has no unit\") => {\n        clear_cache_dir();\n        recompile_without_reuse_plan()\n    }\n    other => other?,\n}","preventionTips":["Never mutate the .baml file set while a compile is running.","Clear the cache after adding/removing/renaming many files at once.","On InvalidData from store paths, fall back to a full uncached compile rather than retrying reuse.","Keep cache manifests and unit blobs written atomically to avoid partially-updated state."],"tags":["cache","internal","consistency"],"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-14T05:17:10.506Z"}