BoundaryML/baml · error · std::io::Error
duplicate compilation unit for `{}`
Error message
duplicate compilation unit for `{}` What it means
baml_cli's bytecode cache refuses to store artifacts when the compile produced two CompilationUnits with the same source_file. The units map is keyed by root-relative source path so each current project file maps to exactly one unit; a collision means the compiler assembled its unit list inconsistently, which is a programmer/internal invariant error rather than something user input normally causes.
Source
Thrown at baml_language/crates/baml_cli/src/bytecode_cache.rs:1587
CompiledUnits::Fresh(units) => (units, false),
CompiledUnits::Reused(units) => (units, true),
CompiledUnits::None => {
debug_assert!(false, "cached compile stored without assembled units");
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"compile produced no assembled units",
));
}
};
self.store(&compiled.program)?;
let mut units_by_source = HashMap::with_capacity(units.len());
for unit in units {
if units_by_source
.insert(unit.source_file.as_str(), unit)
.is_some()
{
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("duplicate compilation unit for `{}`", unit.source_file),
));
}
}
let user_files = user_files_with_rel_paths(db, package);
// Only a successful reuse compile returns assembled `units`. A full
// fallback must persist freshly decomposed units for every file rather
// than carrying pointers from the abandoned reuse plan.
let pointer_plan = if reused_units { plan } else { None };
let mut unit_keys = HashMap::with_capacity(user_files.len());
let mut unit_entries_written = 0usize;
for (_, rel) in &user_files {
if let Some(key) = pointer_plan
.filter(|plan| plan.clean_files.contains(rel))
.and_then(|plan| plan.unit_keys.get(rel))
{View on GitHub (pinned to bd85ce9dee)
Solutions
- Identify the duplicated source path in the message and remove one of the two ways it enters the build (duplicate include, symlink alias, double registration).
- Run a clean rebuild: delete the baml cache directory and recompile so no stale/reused units are carried in.
- If it reproduces on a clean tree, report it upstream with the project layout — this is an internal invariant violation in unit assembly.
- Check for symlinks or case-insensitive-filesystem aliases that make one file reachable under two paths and normalize them.
Example fix
// before: same file registered twice compile(&["schema.baml", "./schema.baml"]) // after: deduplicate paths before compiling let files: BTreeSet<PathBuf> = ["schema.bml".into(), "./schema.baml".into()].into_iter().collect();
Defensive patterns
Strategy: validation
Validate before calling
let mut seen = std::collections::HashSet::new();
for f in &input_files {
let canonical = f.canonicalize()?;
if !seen.insert(canonical) {
return Err(format!("duplicate source file: {}", f.display()));
}
} Type guard
fn all_paths_unique(files: &[PathBuf]) -> bool {
let canon: Vec<_> = files.iter().filter_map(|f| f.canonicalize().ok()).collect();
canon.len() == canon.iter().collect::<std::collections::HashSet<_>>().len()
} Prevention
- Canonicalize paths before registering sources so symlinks/aliases collapse to one entry.
- Deduplicate the file list (BTreeSet/HashSet) before invoking the compiler.
- Avoid feeding the same file via include globs and explicit lists simultaneously.
- Report occurrences on a clean tree upstream — this signals an internal bug, not user error.
When it happens
Trigger: store_artifacts_with_manifest (via verify_and_store) receives a CompiledArtifacts whose units slice contains two entries with identical unit.source_file strings — e.g. the same .baml file was added to the unit list twice during assembly (duplicated user_files entry, symlink/alias resolving to one file under two names, or a bug in the unit-collection pass).
Common situations: A file referenced through two different paths that normalize to the same source_file; a plugin/generator or test harness feeding the same file to the compile twice; a corrupt or hand-edited cache state being replayed; a compiler bug in unit assembly after recent changes to how files are enumerated.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- compiled output has no unit for `{rel}`
- unit source path is not root-relative: `{rel}`
- honest interface fragment for `{}` failed to serialize: {e}
- type_arg_templates count fits in u16
- ntypeargs fits in u16
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/ab5d580b08be3380.
Report an issue: GitHub.