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

  1. 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).
  2. Run a clean rebuild: delete the baml cache directory and recompile so no stale/reused units are carried in.
  3. If it reproduces on a clean tree, report it upstream with the project layout — this is an internal invariant violation in unit assembly.
  4. 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

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


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/ab5d580b08be3380. Report an issue: GitHub.