rtk-ai/rtk · error

dotnet: no subcommand specified

Error message

dotnet: no subcommand specified

What it means

RTK's dotnet passthrough handler requires at least one subcommand. run_passthrough bails before spawning `dotnet` when the args slice is empty, because a bare `rtk dotnet` has nothing to proxy or filter. The real `dotnet` CLI would print its help banner here, but RTK refuses rather than delegate an empty invocation.

Source

Thrown at src/cmds/dotnet/dotnet_cmd.rs:79

    timer.track(
        &format!("dotnet format {}", args.join(" ")),
        &format!("rtk dotnet format {}", args.join(" ")),
        &raw,
        shown,
    );

    if cleanup_report_path {
        if let Some(path) = report_path.as_deref() {
            cleanup_temp_file(path);
        }
    }

    Ok(result.exit_code)
}

pub fn run_passthrough(args: &[OsString], verbose: u8) -> Result<i32> {
    if args.is_empty() {
        anyhow::bail!("dotnet: no subcommand specified");
    }

    let timer = tracking::TimedExecution::start();
    let subcommand = args[0].to_string_lossy().to_string();

    let mut cmd = resolved_command("dotnet");
    cmd.env(DOTNET_CLI_UI_LANGUAGE, DOTNET_CLI_UI_LANGUAGE_VALUE);
    cmd.arg(&subcommand);
    for arg in &args[1..] {
        cmd.arg(arg);
    }

    if verbose > 0 {
        eprintln!("Running: dotnet {} ...", subcommand);
    }

    let result =
        exec_capture(&mut cmd).with_context(|| format!("Failed to run dotnet {}", subcommand))?;

View on GitHub (pinned to d977e1c316)

Solutions

  1. Run rtk with an explicit dotnet subcommand: `rtk dotnet build`, `rtk dotnet test`, `rtk dotnet run`
  2. If you only wanted dotnet's own help/banner, delegate unfiltered: `rtk proxy dotnet` or plain `dotnet --help`
  3. Guard wrapper scripts before forwarding: `[ $# -gt 0 ] || { echo 'usage: rtk dotnet <subcommand>' >&2; exit 2; }`

Example fix

# before
rtk dotnet            # args.is_empty() -> bail!("dotnet: no subcommand specified")

# after
rtk dotnet build --no-restore
# or let the bare CLI through unfiltered (still tracked in rtk gain)
rtk proxy dotnet
Defensive patterns

Strategy: validation

Validate before calling

bash:
# before invoking rtk, make sure a subcommand exists
if [ $# -eq 0 ]; then
  echo "usage: rtk dotnet <subcommand> [args...]" >&2
  exit 2
fi
rtk dotnet "$@"

Type guard

rust:
fn has_dotnet_subcommand(args: &[std::ffi::OsString]) -> bool {
    !args.is_empty()
}

Try / catch

rust:
match dotnet::run_passthrough(&args, verbose) {
    Err(e) if e.to_string().contains("dotnet: no subcommand specified") => {
        eprintln!("usage: rtk dotnet <subcommand> [args...]");
        std::process::exit(2);
    }
    result => result?,
}

Prevention

When it happens

Trigger: Running `rtk dotnet` with zero arguments, which hits `args.is_empty()` at src/cmds/dotnet/dotnet_cmd.rs:80. Typical producers: a wrapper script or alias forwarding an unset/empty variable (`rtk dotnet $DOTNET_ARGS` with DOTNET_ARGS empty), or an agent tool-call that drops the subcommand.

Common situations: CI scripts that assemble dotnet arguments dynamically and forward an empty vector; shell functions with `set -u` disabled letting an empty var expand to nothing; typos like `rtk dotnet ''`; wrapper scripts around `rtk dotnet "$@"` invoked with no positional args.

Related errors


AI-assisted analysis of rtk-ai/rtk@d977e1c316 (2026-08-16). Data as JSON: /api/errors/bf8c8725aa0f4ef9. Report an issue: GitHub.