{"record":{"id":"d42ced4437138288","repo":"jdx/mise","slug":"no-formula-subclass-found","errorCode":null,"errorMessage":"no Formula subclass found","messagePattern":"no Formula subclass found","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/tap_formula_metadata.rb","lineNumber":216,"sourceCode":"    def bottle(*) = nil\n    def head(*) = nil\n    def livecheck(*) = nil\n    def service(*) = nil\n    def test(*) = nil\n    def method_missing(*) = nil\n    def respond_to_missing?(*) = true\n  end\nend\n\ndef inferred_version(url)\n  basename = File.basename(url.to_s).sub(/\\.(tar\\.(gz|xz|bz2|zst)|tgz|txz|zip|gz)\\z/i, \"\")\n  match = basename.match(/(?:^|[-_v])([0-9]+(?:\\.[0-9A-Za-z]+)+(?:[-_.][0-9A-Za-z]+)*)/)\n  match && match[1]\nend\n\neval(STDIN.read.force_encoding(\"UTF-8\"), TOPLEVEL_BINDING, FORMULA_FILE, 1)\nklass = Formula.instance_variable_get(:@subclass)\nraise \"no Formula subclass found\" unless klass\nraise \"formula has no stable URL\" if klass.source_url.to_s.empty?\nraise \"formula has no stable sha256\" if klass.source_sha256.to_s.empty?\nversion = (klass.explicit_version || inferred_version(klass.source_url)).to_s\nraise \"could not infer formula version; add an explicit version declaration\" if version.to_s.empty?\n\nmetadata = {\n  \"name\" => FORMULA_NAME,\n  \"tap\" => ENV.fetch(\"MISE_BREW_TAP\"),\n  \"versions\" => { \"stable\" => version },\n  \"revision\" => klass.revision_value || 0,\n  \"keg_only\" => klass.keg_only_value || false,\n  \"dependencies\" => klass.runtime_dependencies || [],\n  \"build_dependencies\" => klass.build_dependencies || [],\n  \"bottle\" => {},\n  \"urls\" => { \"stable\" => { \"url\" => klass.source_url, \"checksum\" => klass.source_sha256 } },\n  \"ruby_source_path\" => ENV.fetch(\"MISE_BREW_SOURCE_PATH\"),\n  \"ruby_source_checksum\" => { \"sha256\" => ENV.fetch(\"MISE_BREW_SOURCE_CHECKSUM\") },\n  \"tap_git_head\" => ENV.fetch(\"MISE_BREW_TAP_COMMIT\")","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/packages/brew/tap_formula_metadata.rb#L198-L234","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check that MISE_BREW_SOURCE_PATH points at an actual Homebrew formula Ruby file containing `class X < Formula`","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","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)","Re-clone/refresh the tap so the file is intact"],"exampleFix":"// before: file content piped to the extractor\n//   <html><body>404 Not Found</body></html>\n// after: pipe the real formula file\n//   class Wget < Formula\n//     url \"https://ftp.gnu.org/gnu/wget/wget-1.25.0.tar.gz\"\n//     sha256 \"...\"\n//   end","handlingStrategy":"validation","validationCode":"content = File.read(formula_path)\nraise \"#{formula_path} is not a formula file\" unless content.match?(/^\\s*class\\s+\\w+\\s*<\\s*Formula\\b/)","typeGuard":"def formula_file?(path)\n  content = (File.read(path) rescue nil) || \"\"\n  content.match?(/^\\s*class\\s+\\w+\\s*<\\s*Formula\\b/)\nend","tryCatchPattern":"begin\n  extract_formula_metadata(formula_path)\nrescue RuntimeError => e\n  raise unless e.message == \"no Formula subclass found\"\n  warn \"#{formula_path} does not define a Formula subclass; check tap path and fetch\"\nend","preventionTips":["Validate MISE_BREW_SOURCE_PATH exists and starts with `class ... < Formula` before piping it to the extractor","Check for HTML error pages / empty files when fetching formula sources from taps","Match formula token to file path carefully (Formula/<name>.rb, including @version suffixes)","Re-clone the tap if the checkout appears truncated or corrupted"],"tags":["homebrew","formula","missing-class","tap"],"backgroundTag":"class-not-found","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}