jdx/mise · error

bootstrap resource dependency cycle: {cycle}

Error message

bootstrap resource dependency cycle: {cycle}

What it means

After Kahn's-algorithm ordering, if not all resources were emitted, the remaining ones form a dependency cycle. The error lists the ids still having incoming edges so the developer can see which resources mutually depend on each other.

Source

Thrown at src/system/resources.rs:295

                for dependent in dependents {
                    let count = incoming
                        .get_mut(dependent)
                        .expect("dependent resource is present");
                    *count -= 1;
                    if *count == 0 {
                        ready.push_back(dependent.clone());
                    }
                }
            }
        }

        if ordered.len() != self.resources.len() {
            let cycle = incoming
                .into_iter()
                .filter_map(|(id, count)| (count > 0).then_some(id.to_string()))
                .collect::<Vec<_>>()
                .join(", ");
            bail!("bootstrap resource dependency cycle: {cycle}");
        }
        Ok(ordered)
    }
}

/// Build the resource plan currently supported by the provisioning engine.
/// Other bootstrap sections will move into this graph as resource adapters land.
pub(crate) async fn plan(
    config: &Config,
    secrets: &super::secrets::SecretValues,
) -> Result<BootstrapPlan> {
    let mut plan = BootstrapPlan::default();
    let accounts = super::accounts::prepare_requests_from_config(config)?;
    let group_states = accounts
        .groups
        .iter()
        .map(|group| (group.name.clone(), group.state))
        .collect::<HashMap<_, _>>();

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Read the listed cycle members from the message
  2. Break the cycle by removing or redirecting one depends_on edge (usually the incorrect parent link)
  3. Ensure parent/child relationships form a DAG (parents first, no back-edges)

Example fix

// before
dir-a.depends_on = ["dir-b"]; dir-b.depends_on = ["dir-a"]
// after
dir-b.depends_on = ["dir-a"]  # drop the back edge
Defensive patterns

Strategy: validation

Validate before calling

// detect cycles before planning
fn has_cycle(id: &str, deps: &HashMap<String,Vec<String>>, seen: &mut HashSet<String>) -> bool {
    if !seen.insert(id.into()) { return true; }
    deps.get(id).map_or(false, |ds| ds.iter().any(|d| has_cycle(d, deps, seen)))
}

Prevention

When it happens

Trigger: Calling `ordered` when resources A depends on B and B (directly or transitively) depends on A, so no valid order exists.

Common situations: Two directories/files in bootstrap config each declaring the other as parent/dependency; a self-dependency after a config refactor.

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 jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/48e8b1fab11cfb9a. Report an issue: GitHub.