Homebrew Install Stuck on Resolving? Here's Why and How to Fix It
When brew install hangs on 'Resolving dependencies', it's usually a slow API call or a complex dependency tree. Here's what's happening and how to fix it.
You run brew install and it just sits there. No progress bar, no error, just a blinking cursor after “Resolving dependencies…” for 10, 20, sometimes 60+ seconds. This is one of the most common frustrations with Homebrew, and it has a few distinct technical causes.
What happens during “Resolving dependencies”
When Homebrew resolves dependencies, it performs several operations in sequence:
- Fetches the formula definition from the Homebrew JSON API or local tap
- Recursively walks the dependency tree, fetching each dependency’s formula in turn
- Checks for conflicts between installed packages and new requirements
- Determines bottle availability for your platform and macOS version
- Resolves optional and recommended dependencies based on your flags
Each of these steps can involve network requests. For a package like opencv or ffmpeg with deep dependency trees (30+ transitive dependencies), Homebrew may issue dozens of sequential HTTP requests to formulae.brew.sh.
Root cause 1: Sequential API calls
The most common reason for the hang is Homebrew’s synchronous dependency resolution. It fetches metadata for each dependency one at a time. If the Homebrew API is slow — which happens during peak hours or when GitHub’s CDN has latency spikes — each request might take 1-3 seconds. Multiply that by 30 dependencies and you’re waiting over a minute before a single byte of an actual package downloads.
You can confirm this by running with verbose output:
brew install -v ffmpeg
In verbose mode, you’ll see individual API requests being made sequentially. Watch for lines like:
==> Fetching dependencies for ffmpeg: aom, aribb24, dav1d, ...
==> Downloading https://formulae.brew.sh/api/formula/aom.json
==> Downloading https://formulae.brew.sh/api/formula/aribb24.json
Each of these is a blocking HTTP request completed before the next one starts.
Root cause 2: DNS resolution delays
Sometimes the hang occurs at an even lower level. If your DNS resolver is slow or the formulae.brew.sh domain takes time to resolve, each API call gets an additional penalty. macOS’s DNS resolution can be particularly problematic on networks with captive portals, VPNs, or misconfigured DNS servers.
Test your DNS resolution time:
time dig formulae.brew.sh +short
# If this takes more than 100ms, DNS is part of your problem
If you’re on a corporate VPN, DNS queries may be routed through a remote resolver, adding 200-500ms per lookup. Since Homebrew may not reuse connections efficiently, this penalty can apply to each individual API call.
Root cause 3: Stale local metadata
If you haven’t run brew update recently, Homebrew may decide to update before resolving dependencies. This triggers the full git pull of the ~700MB homebrew-core repository, which can appear as a hang during the “resolving” phase — especially since Homebrew doesn’t always clearly indicate that an update is running.
Check when you last updated:
brew config | grep "Last commit"
# If this is more than a day old, auto-update may be triggering
Root cause 4: Complex dependency conflicts
Some packages have conditional dependencies or version conflicts that require backtracking. Homebrew’s dependency resolver is not particularly sophisticated — it uses a relatively straightforward recursive algorithm. When conflicts arise (for example, two packages require different versions of [email protected]), the resolver may spend significant time trying different combinations.
Immediate fixes
Disable auto-update for the current command:
HOMEBREW_NO_AUTO_UPDATE=1 brew install ffmpeg
This prevents the hidden brew update that often causes the initial hang. Set it permanently in your shell profile if you prefer to run brew update manually:
export HOMEBREW_NO_AUTO_UPDATE=1
Use --force-bottle to skip source resolution:
brew install --force-bottle ffmpeg
This tells Homebrew to only consider pre-built bottles, skipping the source-build dependency resolution path which can be slower.
Pre-fetch the dependency list:
brew deps --tree ffmpeg
Running this separately lets you see the full dependency tree and identifies which packages are already installed. It also caches some of the API responses, making the subsequent brew install faster.
Update your DNS resolver:
If DNS is the bottleneck, switch to a faster resolver. On macOS:
# Use Cloudflare DNS
networksetup -setdnsservers Wi-Fi 1.1.1.1 1.0.0.1
Or add to /etc/resolv.conf on Linux:
nameserver 1.1.1.1
nameserver 8.8.8.8
Clear and rebuild the cache:
brew cleanup -s
rm -rf "$(brew --cache)"
Corrupted or stale cache entries can cause resolution to hang while Homebrew tries to verify old downloads.
The underlying architecture problem
All of these fixes are workarounds for the same fundamental issue: Homebrew resolves dependencies by making sequential, synchronous HTTP requests to a JSON API, parsing the results in Ruby, and walking the dependency tree one node at a time.
This design means that your total resolution time scales linearly with the depth and breadth of your dependency tree, multiplied by the latency to the Homebrew API server. There’s no parallelism, no pre-computed dependency graph, and no local cache that eliminates network calls entirely.
How stout handles dependency resolution
stout takes a fundamentally different approach. Its SQLite index contains the entire dependency graph pre-computed and stored locally. When you run stout install ffmpeg:
-
No network calls for resolution. The dependency tree is read from the local SQLite database in microseconds. Every formula’s dependencies, bottle URLs, and version constraints are already indexed.
-
Graph resolution in memory. stout builds the full dependency graph in a single pass using the local index. There’s no recursive API fetching — the entire graph is available locally.
-
Parallel bottle fetching. Once the dependency graph is resolved (typically under 50ms), stout downloads all required bottles in parallel using Tokio’s async runtime. Instead of downloading 30 bottles sequentially, it fetches them all at once.
The result is that the “resolving dependencies” phase in stout is essentially instant:
time stout install ffmpeg
# Resolving dependencies... done (0.03s)
# Downloading 28 bottles... done (4.2s)
# Installing... done (2.1s)
# real 0m6.3s
Compare that to Homebrew’s typical 30-90 seconds for the same operation. The dependency resolution that causes the hang in Homebrew isn’t just faster in stout — it’s a different operation entirely. Reading a local database will always beat making 30 sequential HTTP requests.
If you’re tired of staring at “Resolving dependencies…” while nothing visibly happens, the fix isn’t a better network connection or a faster DNS resolver. It’s an architecture that doesn’t need the network for dependency resolution in the first place.
Need Rust performance engineering or AI agent expertise?
Neul Labs — the team behind stout — consults on Rust development, performance optimization, CLI tool design, and AI agent infrastructure. We build fast, reliable systems that ship.