jdx/mise · error · RuntimeError

formula has no stable URL

Error message

formula has no stable URL

What it means

After eval'ing the formula file, mise checks that the Formula subclass recorded a stable source URL via the `url ...` DSL call. If the subclass never called `url` (or called it with nil), `klass.source_url` is nil and this raise fires. mise builds third-party formulas from source, so it requires a resolvable stable download URL.

Source

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

    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. Ensure the formula has a top-level `url "https://..."` inside `stable` (or class body) that executes unconditionally
  2. If the url is inside a conditional block (on_macos/on_arm/on_system), confirm the host env vars (MISE_BREW_OS, arch) match so the branch executes, or move the url out of the conditional
  3. Update the stub extractor if the formula uses an unrecognized DSL method that swallows the url call
  4. Pin/modify the formula to a version with a plain stable url declaration

Example fix

// before
//   class Foo < Formula
//     head "https://example.com/foo.git"  # no stable url
//   end
// after
//   class Foo < Formula
//     url "https://example.com/foo-1.2.3.tar.gz"
//     sha256 "abc123..."
//     head "https://example.com/foo.git"
//   end
Defensive patterns

Strategy: validation

Validate before calling

body = File.read(formula_path)
raise "formula lacks a stable url declaration" unless body.match?(/^\s*url\s+["']https?:\/\//)

Type guard

def has_stable_url?(formula_source)
  formula_source.match?(/^\s*url\s+["'][^"']+["']/)
end

Try / catch

begin
  extract_formula_metadata(formula_path)
rescue RuntimeError => e
  raise unless e.message == "formula has no stable URL"
  warn "Formula has no stable url; it is likely head-only or uses unsupported DSL"
end

Prevention

When it happens

Trigger: The formula declares its source only in a block the extractor skipped (e.g. only inside `head`, a `resource`, or a conditional `on_macos`/`on_arm` block that didn't run on the host), uses a DSL construct the stub swallows via `method_missing` (which returns nil and discards the url), or simply omits `url` in `stable`.

Common situations: Formulas relying on Homebrew features this lightweight stub does not model (e.g. urls set through custom DSL or strategies inside unexecuted conditionals); formulas with only `head` specs and no stable URL; formulas using url only inside `deprecate!`/disabled paths; a formula whose stable block calls url on a platform branch that doesn't match the host.

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/c57c130c32a8b6c0. Report an issue: GitHub.