brew doctor output
Your system is ready to brew.
(reproduced inside the official ghcr.io/homebrew/brew:main Docker image used by brew test-bot on GitHub Actions; doctor output on the runner is clean aside from unrelated Tier notices)
Verification
brew config output
HOMEBREW_VERSION: 5.1.9-6-gca9228e
ORIGIN: https://github.com/Homebrew/brew
Branch: main
HOMEBREW_PREFIX: /home/linuxbrew/.linuxbrew
Homebrew Ruby: 4.0.3 => /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/portable-ruby/4.0.3/bin/ruby
CPU: quad-core 64-bit icelake
Kernel: Linux 6.17.0-1010-azure x86_64 GNU/Linux
OS: Ubuntu 22.04.5 LTS
Host glibc: 2.35
Host libstdc++: 6.0.30
(captured from the ghcr.io/homebrew/brew:main container running on ubuntu-latest; the same failure also reproduces on ubuntu-24.04-arm)
What were you trying to do (and why)?
Run brew test-bot --only-tap-syntax against a third-party tap on Linux as part of a GitHub Actions matrix job (macOS + ubuntu-latest + ubuntu-24.04-arm). The tap contains macOS-only casks that legitimately use the sha256 arm: …, intel: … shorthand together with depends_on macos: ….
What happened (include all command output)?
==> brew test-bot --only-tap-syntax
…
Error: Cask 'sfm@alpha' definition is invalid: invalid 'sha256' value: nil
Error: 1 failed step!
Failing run: https://github.com/moonfruit/homebrew-tap/actions/runs/25418501691/job/72730024519
The offending cask: https://github.com/moonfruit/homebrew-tap/blob/cask/Casks/sfm%40alpha.rb
cask "sfm@alpha" do
arch arm: "Apple", intel: "Intel"
version "1.14.0-alpha.21"
sha256 arm: "acc0eb4…",
intel: "539c681…"
url "https://github.com/SagerNet/sing-box/releases/download/v#{version}/SFM-#{version}-#{arch}.pkg"
…
depends_on macos: ">= :ventura"
…
end
What did you expect to happen?
brew test-bot --only-tap-syntax should not abort on Linux for a cask that:
- is gated by
depends_on macos: …, and
- uses the
sha256 arm:/intel: shorthand which is documented as a macOS-only convenience (intel: is internally aliased to x86_64:, and sha256 only resolves through the macos: branch of on_system_conditional for those keywords).
brew audit already knows it must not audit casks on Linux — Library/Homebrew/dev-cmd/audit.rb does:
errors = os_arch_combinations.flat_map do |os, arch|
next [] if os == :linux
…
end
…but the cask is loaded eagerly, before that filter, when --tap= is used:
https://github.com/Homebrew/brew/blob/master/Library/Homebrew/dev-cmd/audit.rb#L141-L147
elsif args.tap
Tap.fetch(args.tap).then do |tap|
[
tap.formula_files.map { |path| Formulary.factory(path) },
tap.cask_files.map { |path| Cask::CaskLoader.load(path) },
]
end
On Linux that load runs in the host's OS context, where Cask::DSL#sha256(arm:, intel:) resolves through on_system_conditional(linux: on_arch_conditional(arm: arm64_linux, intel: x86_64_linux)) (Library/Homebrew/cask/dsl.rb:503-525). Because the cask only sets arm:/intel:, both arm64_linux and x86_64_linux are nil, so sha256 raises CaskInvalidError: invalid 'sha256' value: nil — even though the audit code itself would have skipped this cask on Linux a few lines later.
Compare with Readall.valid_casks?, which is overridden only on macOS (Library/Homebrew/extend/os/mac/readall.rb); on Linux the base implementation returns true without loading any cask, so brew readall --aliases --os=all --arch=all <tap> is unaffected. The eager load in dev-cmd/audit.rb is the only step in --only-tap-syntax that actually fails.
Step-by-step reproduction instructions (by running brew commands)
Inside ghcr.io/homebrew/brew:main (or any Linux brew install):
brew tap moonfruit/tap https://github.com/moonfruit/homebrew-tap
brew test-bot --only-tap-syntax
# or, more directly:
brew audit --except=installed --tap=moonfruit/tap
Result:
Error: Cask 'sfm@alpha' definition is invalid: invalid 'sha256' value: nil
A minimal cask that triggers the same failure on Linux without any tap setup:
cask \"macos-only-example\" do
version \"1.0\"
sha256 arm: \"0000000000000000000000000000000000000000000000000000000000000000\",
intel: \"1111111111111111111111111111111111111111111111111111111111111111\"
url \"https://example.invalid/x.pkg\"
name \"Example\"
desc \"macOS-only cask\"
homepage \"https://example.invalid/\"
depends_on macos: \">= :ventura\"
pkg \"x.pkg\"
end
Suggested fix
Either:
- defer loading cask files until after the
os == :linux filter (e.g. wrap the load in a SimulateSystem.with(os: :macos, arch:) block, or load with paths instead of objects and only instantiate inside the per-OS loop), or
- on Linux, skip cask collection entirely under
--tap= (mirroring what Readall.valid_casks? already does for brew readall).
Today the only workaround in tap CI is to replace --only-tap-syntax on Linux with manual brew style + brew readall and to drop brew audit --tap= (or to enumerate every formula by name), which is awkward for taps that legitimately mix macOS-only casks with cross-platform formulae.
brew doctoroutputYour system is ready to brew.(reproduced inside the official
ghcr.io/homebrew/brew:mainDocker image used bybrew test-boton GitHub Actions; doctor output on the runner is clean aside from unrelatedTiernotices)Verification
brew updatetwice and am still able to reproduce my issue.brew doctoroutput" above saysYour system is ready to brewor a definitely unrelatedTiermessage.brew install wget.brew configoutput(captured from the
ghcr.io/homebrew/brew:maincontainer running onubuntu-latest; the same failure also reproduces onubuntu-24.04-arm)What were you trying to do (and why)?
Run
brew test-bot --only-tap-syntaxagainst a third-party tap on Linux as part of a GitHub Actions matrix job (macOS + ubuntu-latest + ubuntu-24.04-arm). The tap contains macOS-only casks that legitimately use thesha256 arm: …, intel: …shorthand together withdepends_on macos: ….What happened (include all command output)?
Failing run: https://github.com/moonfruit/homebrew-tap/actions/runs/25418501691/job/72730024519
The offending cask: https://github.com/moonfruit/homebrew-tap/blob/cask/Casks/sfm%40alpha.rb
What did you expect to happen?
brew test-bot --only-tap-syntaxshould not abort on Linux for a cask that:depends_on macos: …, andsha256 arm:/intel:shorthand which is documented as a macOS-only convenience (intel:is internally aliased tox86_64:, andsha256only resolves through themacos:branch ofon_system_conditionalfor those keywords).brew auditalready knows it must not audit casks on Linux —Library/Homebrew/dev-cmd/audit.rbdoes:…but the cask is loaded eagerly, before that filter, when
--tap=is used:https://github.com/Homebrew/brew/blob/master/Library/Homebrew/dev-cmd/audit.rb#L141-L147
On Linux that load runs in the host's OS context, where
Cask::DSL#sha256(arm:, intel:)resolves throughon_system_conditional(linux: on_arch_conditional(arm: arm64_linux, intel: x86_64_linux))(Library/Homebrew/cask/dsl.rb:503-525). Because the cask only setsarm:/intel:, botharm64_linuxandx86_64_linuxarenil, sosha256raisesCaskInvalidError: invalid 'sha256' value: nil— even though the audit code itself would have skipped this cask on Linux a few lines later.Compare with
Readall.valid_casks?, which is overridden only on macOS (Library/Homebrew/extend/os/mac/readall.rb); on Linux the base implementation returnstruewithout loading any cask, sobrew readall --aliases --os=all --arch=all <tap>is unaffected. The eager load indev-cmd/audit.rbis the only step in--only-tap-syntaxthat actually fails.Step-by-step reproduction instructions (by running
brewcommands)Inside
ghcr.io/homebrew/brew:main(or any Linux brew install):Result:
A minimal cask that triggers the same failure on Linux without any tap setup:
Suggested fix
Either:
os == :linuxfilter (e.g. wrap the load in aSimulateSystem.with(os: :macos, arch:)block, or load with paths instead of objects and only instantiate inside the per-OS loop), or--tap=(mirroring whatReadall.valid_casks?already does forbrew readall).Today the only workaround in tap CI is to replace
--only-tap-syntaxon Linux with manualbrew style+brew readalland to dropbrew audit --tap=(or to enumerate every formula by name), which is awkward for taps that legitimately mix macOS-only casks with cross-platform formulae.