jdx/mise · error · RuntimeError

could not infer formula version; add an explicit version dec

Error message

could not infer formula version; add an explicit version declaration

What it means

mise extracts the stable version of a formula from an explicit `version ...` declaration, or falls back to heuristically parsing the version out of the source URL basename (inferred_version). If neither yields a version — no explicit version declaration and the URL's filename doesn't match the version regex — this raise fires, instructing the formula author to add an explicit version declaration.

Source

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

    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")
}
puts JSON.generate(metadata)

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Add an explicit `version "1.2.3"` declaration to the formula's stable spec (most reliable fix)
  2. Use a versioned download URL whose basename contains the dotted version (e.g. foo-1.2.3.tar.gz)
  3. Point the url at a stable versioned release asset instead of latest/main archive endpoints
  4. Check the basename against the extractor's regex `(?:^|[-_v])([0-9]+(\.[0-9A-Za-z]+)+...)` and adjust the artifact naming

Example fix

// before
//   class Foo < Formula
//     url "https://example.com/releases/latest/download/foo.tgz"
//     sha256 "abc..."
//   end
// after
//   class Foo < Formula
//     version "1.2.3"
//     url "https://example.com/releases/download/1.2.3/foo.tgz"
//     sha256 "abc..."
//   end
Defensive patterns

Strategy: validation

Validate before calling

url = File.read(formula_path)[/^\s*url\s+["']([^"']+)["']/, 1]
version = File.read(formula_path)[/^\s*version\s+["']([^"']+)["']/, 1]
version ||= url.to_s.split("/").last.sub(/\.(tar\.(gz|xz|bz2|zst)|tgz|txz|zip|gz)\z/i, "")[/(?:^|[-_v])([0-9]+(?:\.[0-9A-Za-z]+)+)/, 1]
raise "cannot determine formula version" if version.to_s.empty?

Type guard

def version_inferable?(formula_source)
  return true if formula_source.match?(/^\s*version\s+["']/)
  url = formula_source[/^\s*url\s+["']([^"']+)["']/, 1].to_s
  basename = File.basename(url).sub(/\.(tar\.(gz|xz|bz2|zst)|tgz|txz|zip|gz)\z/i, "")
  !basename.match(/(?:^|[-_v])([0-9]+(?:\.[0-9A-Za-z]+)+)/).nil?
end

Try / catch

begin
  extract_formula_metadata(formula_path)
rescue RuntimeError => e
  raise unless e.message.include?("could not infer formula version")
  warn "Add an explicit `version` declaration to #{formula_path} or use a versioned URL"
end

Prevention

When it happens

Trigger: Formula lacks `version "x.y.z"` and its url basename has no recognizable version token (regex requires digit-led dotted segments after start or a [-_v] separator); url points at a repo archive like `main.tar.gz`, `latest.zip`, or `releases/current/download/foo.tgz`; url basename has only a single undotted number (e.g. `foo-2.tar.gz`) which fails the multi-segment pattern.

Common situations: GitHub `archive/refs/heads/main.tar.gz` or `releases/latest/download/...` URLs used by third-party taps; download URLs with opaque names (`foo-nightly.tgz`, `foo.tar.gz?param=1`); formulas relying on Homebrew's automatic version inference from the url where mise's simpler regex can't; renamed artifacts without version substrings.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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