rtk-ai/rtk · error
sbt: no subcommand specified
Error message
sbt: no subcommand specified
What it means
RTK's sbt handler run_other requires a task name and bails on an empty args slice. Named tasks (compile, run, test variants) go through filtered runners; an empty invocation names no task and cannot be proxied to sbt meaningfully.
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 d977e1c316)
Solutions
- Pass a task: `rtk sbt compile`, `rtk sbt test`, or an integration variant like `rtk sbt it:test` (routed through the shared test filter)
- Use `rtk proxy sbt` for sbt's own interactive/bare behavior
- Assert non-empty task in scripts before invoking rtk
Example fix
# before
rtk sbt # bail!("sbt: no subcommand specified")
# after
rtk sbt 'testOnly com.example.FooSpec'
rtk sbt it:test # detected as integration test, filtered like sbt test Defensive patterns
Strategy: validation
Validate before calling
bash: if [ $# -eq 0 ]; then echo "usage: rtk sbt <task> [args...]" >&2 exit 2 fi rtk sbt "$@"
Type guard
rust:
fn has_sbt_task(args: &[std::ffi::OsString]) -> bool {
!args.is_empty()
} Try / catch
rust:
if let Err(e) = sbt_cmd::run_other(&args, verbose) {
if e.to_string().contains("sbt: no subcommand specified") {
eprintln!("usage: rtk sbt <task> (compile | test | it:test | ...)");
std::process::exit(2);
}
return Err(e);
} Prevention
- Name the sbt task explicitly (compile, test, 'testOnly Foo')
- Verify matrix-leg variables that select the task are set before invoking rtk
- Use `rtk proxy sbt` for sbt's interactive shell instead of bare `rtk sbt`
When it happens
Trigger: Running `rtk sbt` with zero arguments (src/cmds/scala/sbt_cmd.rs:129). Also `rtk sbt $SBT_TASK` with SBT_TASK empty, or wrappers invoked without positionals.
Common situations: Build scripts parameterizing the sbt task that end up empty in some matrix leg; agents probing `rtk sbt` for a help screen; copy-pasted commands missing the task.
Related errors
- dotnet: no subcommand specified
- go: no subcommand specified
- gt: no subcommand specified
- --codex cannot be combined with --opencode
- --codex cannot be combined with --claude-md
AI-assisted analysis of rtk-ai/rtk@d977e1c316 (2026-08-16).
Data as JSON: /api/errors/0727a15b06376212.
Report an issue: GitHub.