BoundaryML/baml · error · SourceRootError
dependency ` ` would form a dependency cycle
Error message
dependency `{name}` would form a dependency cycle What it means
Part of `SourceRootError` returned by `ProjectDatabase::add_source_root` / `add_dependency`. Adding the dependency edge would make the dependency graph cyclic (A depends on B depends on ... depends on A). The database keeps the package graph a DAG and refuses the edge.
Solutions
- Remove the back-edge dependency so the graph stays acyclic.
- Split the shared code into a new common package that both depend on.
- Audit the dependency chain with a graph walk before adding the edge.
Example fix
// before
// core depends on ui, and now: ui_spec.depending_on(vec![dep("core")]) -> cycle
// after
// move shared types into a new "shared" package; ui -> shared, core -> shared Defensive patterns
Strategy: validation
Validate before calling
fn would_cycle(deps: &[Dependency], target: SourceRoot, db: &ProjectDatabase) -> bool {
deps.iter().any(|d| {
let mut stack = vec![d.root];
while let Some(r) = stack.pop() {
if r == target { return true; }
stack.extend(db.dependencies_of(r));
}
false
})
} Try / catch
match db.add_source_root(spec) {
Err(SourceRootError::DependencyCycle { name }) => eprintln!("edge `{name}` closes a cycle; drop it"),
Err(e) => eprintln!("root error: {e}"),
Ok(root) => root,
} Prevention
- Keep the package graph a DAG by design; use a shared package for mutual code.
- Walk the transitive dependency graph before adding back-edges.
- Never let a package depend on itself.
When it happens
Trigger: Calling `add_source_root`/`add_dependency` where the new edge closes a cycle among existing roots, e.g. root X declares a dependency on a root that (transitively) already depends on X.
Common situations: Two packages mutually depending on each other after a refactor; cyclic package splits; accidentally naming the package itself as a dependency.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/a53286e68d6b7783.
Report an issue: GitHub.
Appendix: source
Thrown at baml_language/crates/baml_db/src/db.rs:100
/// [`ProjectDatabase::add_dependency`] refused.
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum SourceRootError {
/// A live root already sits at this (canonical) path.
#[error("a source root already exists at this path")]
PathTaken(SourceRoot),
/// The edge name is one no package may declare: a stdlib package's name
/// (already an implicit edge of every package) or a source-level
/// qualifier (`root`, `env`).
#[error("dependency name `{name}` is reserved")]
ReservedDependencyName { name: Name },
/// The root already has an edge under this name.
#[error("dependency `{name}` is declared twice")]
DuplicateDependencyName { name: Name },
/// The edge names a root that is not live in this database.
#[error("dependency `{name}` names a source root that does not exist")]
UnknownDependencyRoot { name: Name },
/// The edge would make the dependency graph cyclic.
#[error("dependency `{name}` would form a dependency cycle")]
DependencyCycle { name: Name },
/// The interface bytes are not a valid `PackageInterface` artifact.
#[error("invalid package interface: {message}")]
InvalidInterface { message: String },
}
/// The main database for BAML projects.
///
/// `ProjectDatabase` owns the Salsa storage directly and implements all the
/// compiler `Db` traits. It provides high-level APIs for:
/// - Source-root management (add/remove roots, longest-prefix lookup)
/// - File management within a root (add/update/remove files)
/// - Diagnostics collection via `check()`
///
/// ## Example
///
/// ```ignore
/// let mut db = ProjectDatabase::new();View on GitHub (pinned to bd85ce9dee)