dbt-labs/dbt-core · error · anyhow

formula path has no filename: {}

Error message

formula path has no filename: {}

What it means

`run` in crates/dbt-ci/src/homebrew/publish.rs derives the Homebrew formula filename from the `--formula` path via `Path::file_name()`. If the path terminates in `..`, is the filesystem root (`/`), or is empty, `file_name()` returns None and the tool bails with this error. The filename is then validated to end in `.rb` before publishing to the tap.

Source

Thrown at crates/dbt-ci/src/homebrew/publish.rs:49

        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("error: {e:#}");
            ExitCode::from(2)
        }
    }
}

fn run(args: HomebrewPublishArgs) -> Result<()> {
    validate_release_version(&args.version)
        .with_context(|| format!("invalid --version {:?}", args.version))?;
    if !args.formula.is_file() {
        bail!("formula file not found: {}", args.formula.display());
    }
    let filename = args
        .formula
        .file_name()
        .and_then(|s| s.to_str())
        .ok_or_else(|| anyhow!("formula path has no filename: {}", args.formula.display()))?
        .to_string();
    if !filename.ends_with(".rb") {
        bail!("formula filename does not end in .rb: {filename}");
    }

    let token = read_token(&args.token_env, args.dry_run)?;
    // For HTTPS tap URLs, build an `http.extraHeader` config knob carrying
    // Basic auth credentials. Non-HTTPS URLs (file://, ssh) carry their
    // own auth.
    let auth_args = build_auth_args(&args.tap_repo, token.as_deref());

    let work = TempDir::new("dbt-ci-brew-publish")?;
    eprintln!("→ git clone {} -> {}", args.tap_repo, work.path().display());
    let mut clone_argv: Vec<OsString> = auth_args.clone();
    clone_argv.push("clone".into());
    if !is_local_path(&args.tap_repo) {
        clone_argv.push("--depth".into());
        clone_argv.push("1".into());

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Pass the full path to the `.rb` formula file (e.g. `Formula/mytool.rb`), not a directory, `/`, or `..`.
  2. Check the CI variable that supplies the formula path resolves to a real file.
  3. If constructing the path in code, use `join("Formula.rs")`-style explicit file names rather than directory-only components.

Example fix

// before
homebrew publish --formula ./Formula/../
// after
homebrew publish --formula ./Formula/dbt-cli.rb
Defensive patterns

Strategy: validation

Validate before calling

let p = std::path::Path::new(&args.formula);
if !p.is_file() || p.file_name().is_none() {
    return Err(format!("--formula must be a file path, got: {}", p.display()));
}
if p.extension() != Some(std::ffi::OsStr::new("rb")) {
    return Err("--formula must end in .rb".into());
}

Type guard

fn is_formula_path(p: &Path) -> bool {
    p.file_name().is_some() && p.extension() == Some(std::ffi::OsStr::new("rb"))
}

Try / catch

match publish_homebrew(&args) {
    Err(e) if e.to_string().contains("formula path") => {
        eprintln!("pass the full .rb file path, e.g. Formula/dbt-cli.rb")
    }
    Err(e) => return Err(e),
    Ok(v) => v,
}

Prevention

When it happens

Trigger: Passing `--formula /`, `--formula ..`, or a path whose last component is `..`/`.` so `Path::file_name()` yields None; also any path built programmatically from a directory rather than a file.

Common situations: A CI variable interpolating to an empty or root path; shell expansions collapsing a path to `/`; hand-writing a path ending in a trailing `/..`; pointing the publisher at a directory instead of the formula file.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/8d210ca212e50370. Report an issue: GitHub.