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

unit source path is not root-relative: `{rel}`

Error message

unit source path is not root-relative: `{rel}`

What it means

The bytecode cache requires every unit's source_file to be a path relative to the package root, because manifest entries and cache keys are built from root-relative paths. When a unit's source_file is absolute for a file whose manifest key `rel` is relative, the two can never be reconciled, so storing is refused with InvalidData.

Source

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

        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)
        ));

        let mut files: Vec<ManifestFile> = Vec::with_capacity(user_files.len());
        for (sf, rel) in user_files {
            // The file's assembled unit carries its emit-recorded reference
            // edges (`CompilationUnit::referenced_names` / `bakes_type_layout`)
            // — recorded by codegen at its resolution sites, so direct calls

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Register source files with paths relative to the package/source root (strip the root prefix before constructing the unit).
  2. Fix the caller that builds CompilationUnits to canonicalize against the project root rather than passing absolute paths.
  3. Check custom build scripts or CI wrappers that pass absolute paths for the baml sources.
  4. If caused by a library-internal path join, report it upstream with the platform and invocation.

Example fix

// before
CompilationUnit { source_file: "/home/user/proj/schema.baml", .. }
// after
CompilationUnit { source_file: "schema.baml", .. } // root-relative
Defensive patterns

Strategy: validation

Validate before calling

for unit in &units {
    if Path::new(&unit.source_file).is_absolute() {
        return Err(format!("unit source must be root-relative: {}", unit.source_file));
    }
}

Type guard

fn is_root_relative(p: &str) -> bool {
    !std::path::Path::new(p).is_absolute()
}

Try / catch

match store_result {
    Err(e) if e.to_string().contains("not root-relative") => {
        eprintln!("fix source registration to use root-relative paths");
        std::process::exit(1);
    }
    other => other?,
}

Prevention

When it happens

Trigger: store_artifacts_with_manifest finds an assembled unit via units_by_source whose unit.source_file is an absolute path (e.g. a file was registered into the compile with its absolute filesystem path instead of the path relative to the SourceRoot).

Common situations: A build script or harness constructs CompilationUnits from absolute paths (e.g. from an env var or absolute CLI argument) instead of paths relative to the project root; a tool running on Windows/CI passes drive-letter absolute paths into the unit list.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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