Skip to content

azd update banner suggests a brew cask command that fails on macOS (untrusted tap) #8683

Description

@paulyuk

What I saw

azd 1.25.4 prints this update banner on every command:

Update available: 1.25.4 -> 1.25.6
To update, run `brew uninstall azd && brew install --cask azure/azd/azd`

Following the suggested command fails:

$ brew uninstall azd && brew install --cask azure/azd/azd
Error: Refusing to load cask azure/azd/azd from untrusted tap azure/azd.
Run `brew trust --cask azure/azd/azd` or `brew trust azure/azd` to trust it.

Every obvious recovery a user might try also fails — including the ones that should let them escape the situation:

$ brew install azd
Error: Refusing to load cask azure/azd/azd from untrusted tap azure/azd.

$ brew upgrade azd
Error: Refusing to load cask azure/azd/azd from untrusted tap azure/azd.

$ brew uninstall --cask --force azd
Error: Refusing to load cask azure/azd/azd from untrusted tap azure/azd.

$ brew untap azure/azd
Error: Refusing to untap azure/azd because it contains the following installed formulae or casks:
  azd

The user can neither upgrade nor uninstall nor untap. Every brew command that touches azd re-loads the cask definition from the untrusted tap and is blocked. There is no command in the banner, in azd --help, or in any error message that gets the user unstuck. They have to know about brew trust independently.

Root cause

Two independent changes converged:

  1. Homebrew added an "untrusted tap" guard for any tap outside homebrew/core and homebrew/cask. The azure/azd tap (https://github.com/Azure/homebrew-azd) is now treated as untrusted by default. The guard fires on any operation that reads cask metadata — install, upgrade, and uninstall. So the guard also blocks the natural escape path.
  2. azd is also distributed via homebrew/core as the azure-dev formula (Microsoft-maintained), with azd registered as an alias. But because the cask is also named azd, bare brew install azd resolves to the cask path first and trips the trust guard before the core alias ever gets a chance.

Net effect: every macOS user with azd installed via the cask path (which has been the historically recommended path, and what the banner still tells them to use) is wedged. The banner sends them in a circle with no working command.

Repro

  1. Have any azd < 1.25.6 installed via the cask path (brew install --cask azure/azd/azd from the historical docs)
  2. Run any azd command → banner appears
  3. Try any of: brew upgrade azd, brew install azd, brew uninstall --cask --force azd, brew untap azure/azd → all fail with Refusing to load cask azure/azd/azd from untrusted tap azure/azd

Working manual fix (verified end-to-end)

Step 1 — get unstuck on 1.25.6 immediately (no brew trust needed):

brew install --formula azure-dev      # installs 1.25.6 unlinked (cask's azd binary blocks the link)
brew link --overwrite azure-dev       # azd 1.25.6 now active
azd version                           # 1.25.6

This works because brew link --overwrite is a formula operation and never reads cask metadata, so the trust guard doesn't fire. The cask is now dormant — azd runs from the formula.

Step 2 — clean up the leftover cask + tap so future brew upgrade (no args) doesn't keep failing. This step DOES require brew trust, because uninstalling the cask reads its metadata:

brew trust azure/azd                  # one-time trust grant
brew uninstall --cask azd
brew untap azure/azd

After step 2, the tap is gone, future brew install azd resolves cleanly to the homebrew/core formula via its azd alias, and brew upgrade Just Works.

Skipping step 2 leaves the user on 1.25.6 but with a Homebrew state that re-trips the trust guard on the next brew upgrade cycle.

Suggested fix

The current banner sends every cask-installed user — which is most users, since brew install --cask azure/azd/azd was the documented path — into a state where none of the commands they'd naturally try will work. The fact that even brew uninstall --cask azd and brew untap azure/azd are blocked makes this worse: there's no in-tooling escape hatch.

Two options for the banner:

Option A — recommend migrating to the homebrew/core formula (cleanest long-term; eliminates the third-party tap entirely; works whether the user is currently on cask or formula):

A new version of azd (1.25.6) is available.
To update, run:
  brew install --formula azure-dev && brew link --overwrite azure-dev
For a full cleanup (recommended), then run:
  brew trust azure/azd && brew uninstall --cask azd && brew untap azure/azd

Option B — detect install method, emit matching command, and never emit a cask command without the trust prerequisite:

  • Formula install → brew upgrade --formula azure-dev
  • Cask install → brew trust azure/azd && brew upgrade --cask azd

Either avoids the dead-end the current banner produces. Option A is preferable because it also fixes the underlying problem (third-party tap going forward) instead of just papering over it.

If the team wants to keep azure/homebrew-azd as the primary distribution channel, the azure/homebrew-azd README and the Homebrew tap landing page should also document the new brew trust azure/azd step prominently — Homebrew's tap-trust change broke installs from the tap, not just upgrades, so new users hitting the historical install command will also be stuck without it.

Environment

  • macOS (Apple Silicon, Tahoe)
  • azd 1.25.4 → 1.25.6
  • Homebrew current
  • Originally installed via brew install --cask azure/azd/azd (historically recommended path)
  • Banner appears on every azd invocation, including scripts that shell out to azd (e.g. azd env get-values inside other tools' bootstrap scripts)

Metadata

Metadata

Labels

area/installerInstall scripts, MSI, self-updatebugSomething isn't workingregressionBroke something that worked before

Type

Fields

No fields configured for Bug.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions