jdx/mise · error
brew-cask:{}: dependency cycle detected
Error message
brew-cask:{}: dependency cycle detected What it means
While recursively installing a cask's dependency chain, Homebrew tracks ancestor cask tokens; if a cask appears in its own ancestor set the chain is cyclic and installation is aborted. This prevents infinite recursion when cask `depends_on cask:` declarations form a loop.
Source
Thrown at src/system/packages/brew/cask/mod.rs:555
self.install_one_with_ancestors(req, opts, pr, &BTreeSet::new(), manager_options, mode)
.await
}
/// Installs a cask while detecting dependency cycles and preserving ownership.
/// Dependencies use install mode. The requested cask's eligibility is checked
/// before staging and again under the installation lock before replacement.
async fn install_one_with_ancestors(
&self,
req: &PackageRequest,
opts: &InstallOpts,
pr: Option<&dyn SingleReport>,
ancestors: &BTreeSet<String>,
manager_options: &ManagerPackageOptions,
mode: InstallMode,
) -> Result<String> {
let cask = fetch_cask(req, !opts.dry_run).await?;
if ancestors.contains(&cask.token) {
bail!("brew-cask:{}: dependency cycle detected", cask.token);
}
let mut ancestors = ancestors.clone();
ancestors.insert(cask.token.clone());
if let Some(version) = homebrew_installed_version(&cask.token)? {
info!(
"brew-cask:{}: installed and managed by Homebrew; leaving unchanged",
cask.token
);
return Ok(version);
}
let artifacts = cask_artifacts(&cask)?;
validate_platform_support(&cask, &artifacts)?;
let installed_version = mise_installed_cask_version(&cask)?;
if let Some(reason) =
installed_skip_reason(&cask, &artifacts, installed_version.as_deref(), mode)?
{
info!("brew-cask:{}: {reason}", cask.token);
return Ok(reason.to_string());View on GitHub (pinned to afd2eddd3a)
Solutions
- Inspect the cask's `depends_on cask:` declarations to find the cycle
- Fix the tap/cask by removing the erroneous dependency edge
- Install the problematic cask directly if its dependency is spurious
- Report the cycle to the tap maintainer if it's a third-party cask
Example fix
// before (cask B) depends_on cask: "a" # while cask A depends_on cask: "b" // after (cask B) # remove the circular depends_on line
Defensive patterns
Strategy: validation
Validate before calling
fn cycle_check(token: &str, ancestors: &BTreeSet<String>) -> Result<()> {
if ancestors.contains(token) {
Err(format!("dependency cycle at {token}"))
} else { Ok(()) }
} Try / catch
match install_cask(req).await {
Err(e) if e.to_string().contains("dependency cycle detected") => {
eprintln!("inspect depends_on cask: chain for {}", req.name);
Err(e)
}
r => r,
} Prevention
- Review `depends_on cask:` edges in tap for acyclicity before merging
- Keep dependency graphs shallow in custom taps
- Test cask installs in CI to surface cycles early
When it happens
Trigger: install_one (or the recursive dependency walker) encounters a cask whose token is already present in the `ancestors` BTreeSet — e.g. cask A depends on B and B depends on A, or a self-dependency.
Common situations: Third-party taps with mis-declared `depends_on cask:` stanzas; newly edited casks introducing a cycle; users installing two casks whose dependency sets reference each other.
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
- brew-cask: structured move source '{}' was not found
- brew-cask: structured flight glob '{}' matched outside stage
- brew-cask: structured file operation must use staged_path
- brew-cask: invalid structured flight path '{}'
- brew casks are installed at their current version ('{p}')
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/25614116e268aa55.
Report an issue: GitHub.