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
- Update/refresh the tap so fresh cask JSON is fetched (mise caches HTTP JSON; clear the cache if needed)
- Inspect the cask JSON's depends_on fields and report the loop to the tap maintainer
- 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
- Pin taps you control to reviewed revisions with sane depends_on metadata
- When authoring taps, never let cask dependencies reference an ancestor
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
- brew-cask:{}: Homebrew owns this cask; remove it with Homebr
- brew-cask:{}: conflicts with installed cask {}
- brew-cask:{}: Homebrew took ownership of this cask while ins
- brew casks are installed at their current version ('{p}')
- brew-cask: unsupported tap URL for '{name}'; only GitHub tap
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/2b44893c2de501f5.
Report an issue: GitHub.