jdx/mise · error

brew-cask:{}: dependency cycle detected

Error message

brew-cask:{}: dependency cycle detected

What it means

While installing a Homebrew cask, mise walks the dependency chain passing along the set of ancestor casks already being installed. If the cask about to be installed is already in that set, the chain loops and mise aborts with this cycle error instead of recursing forever or stack-overflowing.

Source

Thrown at src/system/packages/brew/cask.rs:377

        &self,
        req: &PackageRequest,
        opts: &InstallOpts,
        pr: Option<&dyn SingleReport>,
    ) -> Result<String> {
        self.install_one_with_ancestors(req, opts, pr, &BTreeSet::new())
            .await
    }

    async fn install_one_with_ancestors(
        &self,
        req: &PackageRequest,
        opts: &InstallOpts,
        pr: Option<&dyn SingleReport>,
        ancestors: &BTreeSet<String>,
    ) -> Result<String> {
        let cask = fetch_cask(req).await?;
        if ancestors.contains(&cask.token) {
            bail!("brew-cask:{}: dependency cycle detected", cask.token);
        }
        let mut ancestors = ancestors.clone();
        ancestors.insert(cask.token.clone());
        let artifacts = cask_artifacts(&cask)?;
        validate_platform_support(&cask, &artifacts)?;
        if homebrew_metadata_present(&cask.token) {
            bail!(
                "brew-cask:{}: Homebrew owns this cask; remove it with Homebrew before installing it with mise",
                cask.token
            );
        }
        if installed_cask_version(&cask, &artifacts)?.as_deref() == Some(cask.version.as_str()) {
            info!("brew-cask:{}: already installed", cask.token);
            return Ok(cask.version);
        }
        for conflict in &cask.conflicts_with.cask {
            if !installed_versions(conflict).is_empty() {
                bail!(

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Update/refresh the tap so fresh cask JSON is fetched (mise caches HTTP JSON; clear the cache if needed)
  2. Inspect the cask JSON's depends_on fields and report the loop to the tap maintainer
  3. Install the cask with the brew CLI until the tap is fixed
Defensive patterns

Strategy: validation

Validate before calling

// Before batch-installing casks, walk the declared dependency graph and reject cycles:
fn has_cycle(start: &str, deps: &dyn Fn(&str) -> Vec<String>) -> bool {
    fn visit(node: &str, stack: &mut Vec<String>, deps: &dyn Fn(&str) -> Vec<String>) -> bool {
        if stack.iter().any(|n| n == node) { return true; }
        stack.push(node.to_string());
        let r = deps(node).iter().any(|next| visit(next, stack, deps));
        stack.pop();
        r
    }
    visit(start, &mut vec![], deps)
}

Try / catch

Catch "dependency cycle detected" per cask and skip/report that cask instead of failing the whole batch; cycles are metadata bugs, not transient states, so retrying unchanged will not help.

Prevention

When it happens

Trigger: Installing a cask whose depends_on chain (as published in the tap's cask JSON) leads back to itself — e.g. cask A depends on cask B which depends on A, typically from a tap author editing dependency metadata incorrectly.

Common situations: Third-party taps with hand-written dependency metadata; renames that turned two casks into mutual dependents; stale cached tap data after an upstream restructure.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/2b44893c2de501f5. Report an issue: GitHub.