BoundaryML/baml · error · std::io::Error

compiled output has no unit for `{rel}`

Error message

compiled output has no unit for `{rel}`

What it means

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.

Source

Thrown at baml_language/crates/baml_cli/src/bytecode_cache.rs:1610

        }

        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))
            {
                unit_keys.insert(rel.clone(), *key);
                continue;
            }
            let Some(unit) = units_by_source.get(rel.as_str()) else {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidData,
                    format!("compiled output has no unit for `{rel}`"),
                ));
            };
            if std::path::Path::new(&unit.source_file).is_absolute() {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidData,
                    format!("unit source path is not root-relative: `{rel}`"),
                ));
            }
            let (key, wrote) = self.cache.store_unit_shared(unit)?;
            unit_entries_written += usize::from(wrote);
            unit_keys.insert(rel.clone(), *key.as_bytes());
        }
        cache_debug(format_args!(
            "unit store: wrote {unit_entries_written}, reused {}",
            user_files.len().saturating_sub(unit_entries_written)
        ));

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Delete the baml bytecode cache directory and rebuild cleanly so no reuse plan or stale units are replayed.
  2. Make sure no build process mutates the project's .baml files (add/rename/delete) while a compile is in flight.
  3. Re-run from a clean checkout if a stale generated tree or leftover manifest is suspected.
  4. If it reproduces deterministically on a clean cache, file a bug with the repro — the unit assembly / reuse-plan pairing is violating its contract.

Example fix

// before: reusing a plan assembled before new files were added
store_artifacts_with_manifest(&db, pkg, &compiled, &fresh, Some(&stale_plan))
// after: fall back to a full compile so all units are freshly assembled
store_artifacts_with_manifest(&db, pkg, &compiled, &fresh, None)
Defensive patterns

Strategy: validation

Validate before calling

let unit_sources: HashSet<&str> = compiled.units.iter().map(|u| u.source_file.as_str()).collect();
for (_, rel) in &user_files {
    assert!(unit_sources.contains(rel.as_str()), "no assembled unit for {rel}");
}

Type guard

fn units_cover_all_files(units: &[CompilationUnit], rels: &[String]) -> bool {
    let have: HashSet<&str> = units.iter().map(|u| u.source_file.as_str()).collect();
    rels.iter().all(|r| have.contains(r.as_str()))
}

Try / catch

match result {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData
        && e.to_string().contains("compiled output has no unit") => {
        clear_cache_dir();
        recompile_without_reuse_plan()
    }
    other => other?,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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/7a16cc2726b9cf99. Report an issue: GitHub.