jdx/mise · error · RuntimeError

no Formula subclass found

Error message

no Formula subclass found

What it means

mise's formula metadata extractor evals a Homebrew formula DSL file and expects that evaluating it defines a subclass of the stub `Formula` class (via Homebrew's `class Foo < Formula` pattern, captured in `Formula.inherited`). If the evaluated file never defines a Formula subclass, `Formula.instance_variable_get(:@subclass)` is nil and this raise fires. This means the file on STDIN is not a recognizable Homebrew formula.

Source

Thrown at src/system/packages/brew/tap_formula_metadata.rb:216

    def bottle(*) = nil
    def head(*) = nil
    def livecheck(*) = nil
    def service(*) = nil
    def test(*) = nil
    def method_missing(*) = nil
    def respond_to_missing?(*) = true
  end
end

def inferred_version(url)
  basename = File.basename(url.to_s).sub(/\.(tar\.(gz|xz|bz2|zst)|tgz|txz|zip|gz)\z/i, "")
  match = basename.match(/(?:^|[-_v])([0-9]+(?:\.[0-9A-Za-z]+)+(?:[-_.][0-9A-Za-z]+)*)/)
  match && match[1]
end

eval(STDIN.read.force_encoding("UTF-8"), TOPLEVEL_BINDING, FORMULA_FILE, 1)
klass = Formula.instance_variable_get(:@subclass)
raise "no Formula subclass found" unless klass
raise "formula has no stable URL" if klass.source_url.to_s.empty?
raise "formula has no stable sha256" if klass.source_sha256.to_s.empty?
version = (klass.explicit_version || inferred_version(klass.source_url)).to_s
raise "could not infer formula version; add an explicit version declaration" if version.to_s.empty?

metadata = {
  "name" => FORMULA_NAME,
  "tap" => ENV.fetch("MISE_BREW_TAP"),
  "versions" => { "stable" => version },
  "revision" => klass.revision_value || 0,
  "keg_only" => klass.keg_only_value || false,
  "dependencies" => klass.runtime_dependencies || [],
  "build_dependencies" => klass.build_dependencies || [],
  "bottle" => {},
  "urls" => { "stable" => { "url" => klass.source_url, "checksum" => klass.source_sha256 } },
  "ruby_source_path" => ENV.fetch("MISE_BREW_SOURCE_PATH"),
  "ruby_source_checksum" => { "sha256" => ENV.fetch("MISE_BREW_SOURCE_CHECKSUM") },
  "tap_git_head" => ENV.fetch("MISE_BREW_TAP_COMMIT")

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Check that MISE_BREW_SOURCE_PATH points at an actual Homebrew formula Ruby file containing `class X < Formula`
  2. Inspect the file content — if it's an HTML error page or empty, fix the source URL/fetch that produced it and re-fetch the tap
  3. Verify the formula name/path in the tap (file names differ from formula tokens; e.g. python@3.12 lives at Formula/python@3.12.rb)
  4. Re-clone/refresh the tap so the file is intact

Example fix

// before: file content piped to the extractor
//   <html><body>404 Not Found</body></html>
// after: pipe the real formula file
//   class Wget < Formula
//     url "https://ftp.gnu.org/gnu/wget/wget-1.25.0.tar.gz"
//     sha256 "..."
//   end
Defensive patterns

Strategy: validation

Validate before calling

content = File.read(formula_path)
raise "#{formula_path} is not a formula file" unless content.match?(/^\s*class\s+\w+\s*<\s*Formula\b/)

Type guard

def formula_file?(path)
  content = (File.read(path) rescue nil) || ""
  content.match?(/^\s*class\s+\w+\s*<\s*Formula\b/)
end

Try / catch

begin
  extract_formula_metadata(formula_path)
rescue RuntimeError => e
  raise unless e.message == "no Formula subclass found"
  warn "#{formula_path} does not define a Formula subclass; check tap path and fetch"
end

Prevention

When it happens

Trigger: Piping a file to src/system/packages/brew/tap_formula_metadata.rb that contains no `class Something < Formula` declaration — e.g. an empty file, an HTML error page, a 404 body, a cask file, or a non-Ruby file at MISE_BREW_SOURCE_PATH.

Common situations: The tap path for the formula is wrong so the fetch returned an error page or empty content; the file is a Cask or other Ruby file rather than a formula; tap checkout is broken/truncated; formula renamed and the file no longer exists at the expected path.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/d42ced4437138288. Report an issue: GitHub.