rtk-ai/rtk · error

gt: no subcommand specified

Error message

gt: no subcommand specified

What it means

RTK's gt (Graphite) handler run_other requires at least one subcommand and bails on an empty args slice before doing anything. Known subcommands (create, branch) go through dedicated filtered paths; unknown ones are forwarded to git routing, but an empty invocation has no route at all.

Source

Thrown at src/cmds/git/gt_cmd.rs:127

pub fn run_sync(args: &[String], verbose: u8) -> Result<i32> {
    run_gt_filtered(&["sync"], args, verbose, "gt_sync", filter_gt_sync)
}

pub fn run_restack(args: &[String], verbose: u8) -> Result<i32> {
    run_gt_filtered(&["restack"], args, verbose, "gt_restack", filter_gt_restack)
}

pub fn run_create(args: &[String], verbose: u8) -> Result<i32> {
    run_gt_filtered(&["create"], args, verbose, "gt_create", filter_gt_create)
}

pub fn run_branch(args: &[String], verbose: u8) -> Result<i32> {
    run_gt_filtered(&["branch"], args, verbose, "gt_branch", filter_identity)
}

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

    let subcommand = args[0].to_string_lossy();
    let rest: Vec<String> = args[1..]
        .iter()
        .map(|a| a.to_string_lossy().into())
        .collect();

    // gt passes unknown subcommands to git, so "gt status" = "git status".
    // Route known git commands to RTK's git filters for token savings.
    match subcommand.as_ref() {
        "status" => crate::git::run(crate::git::GitCommand::Status, &rest, None, verbose, &[]),
        "diff" => crate::git::run(crate::git::GitCommand::Diff, &rest, None, verbose, &[]),
        "show" => crate::git::run(crate::git::GitCommand::Show, &rest, None, verbose, &[]),
        "add" => crate::git::run(crate::git::GitCommand::Add, &rest, None, verbose, &[]),
        "push" => crate::git::run(crate::git::GitCommand::Push, &rest, None, verbose, &[]),
        "pull" => crate::git::run(crate::git::GitCommand::Pull, &rest, None, verbose, &[]),
        "fetch" => crate::git::run(crate::git::GitCommand::Fetch, &rest, None, verbose, &[]),

View on GitHub (pinned to d977e1c316)

Solutions

  1. Pass a subcommand: `rtk gt create`, `rtk gt submit`, or a git-backed one like `rtk gt status` (routed to RTK's git filter)
  2. Use `rtk proxy gt` for gt's own bare-CLI behavior
  3. Check for non-empty args in wrapper scripts before calling rtk

Example fix

# before
rtk gt               # bail!("gt: no subcommand specified")

# after
rtk gt create --merge
# unknown subcommands are forwarded to git, so this also works:
rtk gt status        # == git status, token-optimized
Defensive patterns

Strategy: validation

Validate before calling

bash:
if [ $# -eq 0 ]; then
  echo "usage: rtk gt <subcommand> [args...]" >&2
  exit 2
fi
rtk gt "$@"

Type guard

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

Try / catch

rust:
if let Err(e) = gt_cmd::run_other(&args, verbose) {
    if e.to_string().contains("gt: no subcommand specified") {
        eprintln!("usage: rtk gt <subcommand> (create, submit, or any git command)");
        std::process::exit(2);
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Running `rtk gt` with zero arguments (src/cmds/git/gt_cmd.rs:128 `args.is_empty()`). Also `rtk gt "$GT_CMD"` where GT_CMD is unset, or a wrapper function invoked without positionals.

Common situations: Shell wrappers that conditionally build the gt command and forward it unquoted; agents calling `rtk gt` bare expecting a help screen; scripts copied from a context where the subcommand variable was populated.

Related errors


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