rtk-ai/rtk · error
sbt: no subcommand specified
Error message
sbt: no subcommand specified
What it means
rtk's `sbt` proxy requires at least one argument (task or subcommand) to route output through the correct filter (test, run, it:test, etc.). Invoking `rtk sbt` with no arguments leaves nothing to dispatch, so run_other bails with this message.
Solutions
- Pass an sbt task explicitly: `rtk sbt test`, `rtk sbt run`, `rtk sbt it:test`.
- Fix the invoking script/CI variable so the task name is actually set (guard `[ -n "$TASK" ]`).
- Use `rtk proxy sbt` to run sbt's interactive shell or any unfiltered invocation.
- Run `sbt` directly if you want its own REPL/usage behavior.
Example fix
// before
rtk sbt $TASK # TASK empty -> sbt: no subcommand specified
// after
rtk sbt "${TASK:-test}" # or fail fast: : "${TASK:?TASK must be set}" Defensive patterns
Strategy: validation
Validate before calling
if [ -z "$TASK" ]; then echo "usage: rtk sbt <task> [args]" >&2; exit 2; fi rtk sbt "$TASK" "$@"
Type guard
fn has_args(args: &[OsString]) -> bool { !args.is_empty() } Try / catch
match rtk_result {
Err(e) if e.to_string().contains("sbt: no subcommand") => {
eprintln!("supply an sbt task, e.g. rtk sbt test");
std::process::exit(2);
}
other => other?,
} Prevention
- Always pass an sbt task explicitly in scripts and CI matrices.
- Fail fast on empty task variables with ${TASK:?TASK not set}.
- Use `rtk proxy sbt` for the interactive shell or unfiltered runs.
When it happens
Trigger: Running `rtk sbt` with no task, or calling scala/sbt_cmd::run_other(&[], verbose) with an empty argument vector.
Common situations: CI scripts parameterized like `rtk sbt $TASK` where $TASK is empty; users who expect bare `rtk sbt` to drop into the sbt interactive shell; a Makefile target that lost its TASK argument.
Understand the failure class
Background: "no subcommand specified" and "... is required": CLI errors when a required argument is missing — this error's family across 13 libraries.
Related errors
- at least one of install_claude or install_opencode must be…
- dotnet: no subcommand specified
- go: no subcommand specified
- gt: no subcommand specified
- npx requires a command argument
AI-assisted analysis of rtk-ai/rtk@0924356b4c (2026-09-19).
Data as JSON: /api/errors/0727a15b06376212.
Report an issue: GitHub.
Appendix: source
Thrown at src/cmds/scala/sbt_cmd.rs:128
RunOptions::with_tee(tee_label),
)
}
pub fn run_test(args: &[String], verbose: u8) -> Result<i32> {
run_task(args, "test", filter_sbt_test, "sbt_test", verbose)
}
pub fn run_compile(args: &[String], verbose: u8) -> Result<i32> {
run_task(args, "compile", filter_sbt_compile, "sbt_compile", verbose)
}
pub fn run_run(args: &[String], verbose: u8) -> Result<i32> {
run_task(args, "run", filter_sbt_run, "sbt_run", verbose)
}
pub fn run_other(args: &[OsString], verbose: u8) -> Result<i32> {
if args.is_empty() {
anyhow::bail!("sbt: no subcommand specified");
}
let subcommand = args[0].to_string_lossy().into_owned();
// Integration test commands (it:test, IntegrationTest/test, etc.) produce standard
// ScalaTest output — filter them like `sbt test`, through the shared runner so the
// never_worse cap and tee hint apply (an unrecognized output would otherwise be
// reprinted verbatim plus the hint, i.e. more than the raw command produced).
if is_integration_test_cmd(&subcommand) || is_test_task(&subcommand) {
let mut cmd = resolved_command("sbt");
cmd.arg(&subcommand);
for arg in &args[1..] {
cmd.arg(arg);
}
if verbose > 0 {
eprintln!("Running: sbt {} ...", subcommand);
}View on GitHub (pinned to 0924356b4c)